Не удалось купить с предложением подписки - PullRequest
10 голосов
/ 15 мая 2019

Я пытаюсь заставить работать предложение In-App Purchase.Поэтому я получаю закодированную подпись, nonce, метку времени и идентификатор ключа с нашего сервера.Я создаю объект SKPaymentDiscount и устанавливаю для него paymentDiscount из SKMutablePayment объекта.

В первом всплывающем окне отображается измененная цена, как и ожидалось -> введите пароль и продолжайте -> Второе всплывающее окно: Подтвердите подписку: Ok -> Третье всплывающее окно: отображается следующая ошибка Невозможно приобрести Обратитесь к разработчику за дополнительной информацией.

Я попытался передать неприменимый идентификатор предложения для продукта.Затем он выдал правильную ошибку, сказав: это не может быть применено к этому.

PromoOfferAPI.prepareOffer(usernameHash: "name", productIdentifier: "bundleid.product", offerIdentifier: "TEST10") { (result) in
            switch result {

            case let .success(discount):
                // The original product being purchased.
                let payment = SKMutablePayment(product: option.product)
                // You must set applicationUsername to be the same as the one used to generate the signature.
                payment.applicationUsername = "name"
                // Add the offer to the payment.
                payment.paymentDiscount = discount
                // Add the payment to the queue for purchase.
                SKPaymentQueue.default().add(payment)
                break
            case let .customFail(message):
                print(message)
                break
            case let .failure(error):
                print(error.localizedDescription)
                break
            }
        }

image

Независимо от того, сколько раз я пытаюсь, он продолжает давать мне ту же ошибку. Невозможно приобрести Свяжитесь с разработчиком для получения дополнительной информации. Что можно сделать, чтобы решить эту проблему.Буду признателен за любую оказанную помощь.

Заранее спасибо!

Редактировать 1: никогда не включается в функцию updatedTransactions.Он просто регистрирует Finishing transaction for payment "bundleid.product" with state: failed.

Редактировать 2: Получил ошибку: код - 12 (недопустимая подпись).Не удается подключиться к iTunes Store

Node.JS код, который генерирует закодированную подпись.

const UUID = require("uuid-v4");
const microtime = require('microtime');
const express = require('express');
const router = express.Router();
const EC = require("elliptic").ec;
const ec = new EC("secp256k1");
const crypto = require('crypto');

const privateKey = `-----BEGIN PRIVATE KEY-----
key goes here
-----END PRIVATE KEY-----`;
//const key = ec.keyFromPrivate(privateKey,'hex');


router.post('/',(req, res)=>{
    const bundle_id = "bundle.id";
    const key_id = "keyed";
    const nonce = String(UUID()).toLowerCase();// Should be lower case
    const timestamp = microtime.now();

    const product = req.body.product;
    const offer = req.body.offer;
    const application_username = req.body.application_username;

    const payload = bundle_id + '\u2063' + key_id + '\u2063' + product + '\u2063' + offer + '\u2063' + application_username + '\u2063' + String(nonce) + '\u2063' + String(timestamp)
    let shaMsg = crypto.createHash("sha256").update(payload).digest();
    let signature = ec.sign(shaMsg, privateKey, {canonical: true});
    let derSign = signature.toDER();
    let buff = new Buffer(derSign);  
    let base64EncodedSignature = buff.toString('base64');
    let response = {
        "signeture": base64EncodedSignature,
        "nonce": nonce,
        "timestamp": timestamp,
        "keyIdentifier": key_id
    }
    res.type('json').send(response);
});

module.exports = router;

Ответы [ 2 ]

2 голосов
/ 23 мая 2019

После многих проб и ошибок разобрался с проблемой. В основном это было из-за неправильного алгоритма и мелких проблем тут и там. Вот полный код в Node.js, надеюсь, это кому-нибудь поможет.

  // https://developer.apple.com/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers
  // Step 1
  const appBundleID = req.body.appBundleID
  const keyIdentifier = req.body.keyIdentifier
  const productIdentifier = req.body.productIdentifier
  const offerIdentifier = req.body.offerIdentifier
  const applicationUsername = req.body.applicationUsername

  const nonce = uuid4()
  const timestamp = Math.floor(new Date())

  // Step 2
  // Combine the parameters into a UTF-8 string with 
  // an invisible separator ('\u2063') between them, 
  // in the order shown:
  // appBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + 
  // '\u2063' + offerIdentifier + '\u2063' + applicationUsername + '\u2063' + 
  // nonce + '\u2063' + timestamp

  let payload = appBundleID + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + applicationUsername + '\u2063' + nonce+ '\u2063' + timestamp

  // Step 3
  // Sign the combined string
  // Private Key - p8 file downloaded
  // Algorithm - ECDSA with SHA-256

  const keyPem = fs.readFileSync('file_name.pem', 'ascii');
  // Even though we are specifying "RSA" here, this works with ECDSA
  // keys as well.
  // Step 4
  // Base64-encode the binary signature
  const sign = crypto.createSign('RSA-SHA256')
                   .update(payload)
                   .sign(keyPem, 'base64');

  let response1 = {
    "signature": sign,
    "nonce": nonce,
    "timestamp": timestamp,
    "keyIdentifier": keyIdentifier
  }
  res.type('json').send(response1);
0 голосов
/ 15 июня 2019

Я столкнулся с той же проблемой при тестировании новых файлов сервера Node.js примера WWDC2019, которые они предоставили.После чтения файла я смог успешно сгенерировать подпись.

Однако, к моему удивлению, неверная подпись будет выглядеть так же, как действительная, и мне потребовалось некоторое время, чтобы понять, что моя подпись недействительна.

Моя ошибка была следующей: я использовал Alamofire, чтобы сделать запрос GET на мой сервер, например так:

AF.request("myserver:3000/offer", parameters: parameters).responseJSON { response in

        var signature: String?
        var keyID: String?
        var timestamp: NSNumber?
        var nonce: UUID?

        switch response.result {
        case let .success(value):
            let json = JSON(value)

            // Get required parameters for creating offer
            signature = json["signature"].stringValue
            keyID = json["keyID"].stringValue
            timestamp = json["timestamp"].numberValue
            nonce = UUID(uuidString: json["nonce"].stringValue)

        case let .failure(error):
            print(error)
            return
        }

        // Create offer
        let discountOffer = SKPaymentDiscount(identifier: offerIdentifier, keyIdentifier: keyID!, nonce: nonce!, signature: signature!, timestamp: timestamp!)

        // Pass offer in completion block
        completion(discountOffer) // this completion is a part of the method where this snippet is running
    }
}

На файлах, представленных в видео WWDC2019 по подпискеПредлагает , в файле index.js, они загружают параметры, которые я передал по моему запросу, примерно так:

const appBundleID = req.body.appBundleID;
const productIdentifier = req.body.productIdentifier;
const subscriptionOfferID = req.body.offerID;
const applicationUsername = req.body.applicationUsername;

Однако мой запрос alamofire не передал параметры в теле, а скореев качестве параметров запроса.Поэтому сервер генерировал подпись с нулевым appBundleID, а также с другими пустыми полями!Поэтому я изменил вышеупомянутый раздел index.js следующим образом:

const appBundleID = req.query.appBundleID;
const productIdentifier = req.query.productIdentifier;
const subscriptionOfferID = req.query.offerID;
const applicationUsername = req.query.applicationUsername;

Надеюсь, это поможет всем, кто это упустил.Прошу прощения за мой небезопасный быстрый, но я надеюсь, что вы поняли!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...