Создание подписи для предложений подписки - Xcode - Swift - PullRequest
3 голосов
/ 29 марта 2019

Я хотел бы спросить, реализовал ли кто-то новые Предложения для inapp-подписки (автоматическое продление), сложность создания серверной системы для создания этой подписи с использованием ключа p8 с php, если это возможно. Я нашел это в документации Apple, я не уверен, что понимаю это: https://developer.apple.com/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers

Ответы [ 5 ]

3 голосов
/ 29 марта 2019

Вот пошаговое руководство от RevenueCat: Предложения по подписке iOS

Пост содержит гораздо больше деталей, но генерация подписи:

import json
import uuid
import time
import hashlib
import base64

from ecdsa import SigningKey
from ecdsa.util import sigencode_der

bundle_id = 'com.myapp'
key_id = 'XWSXTGQVX2'
product = 'com.myapp.product.a'
offer = 'REFERENCE_CODE' # This is the code set in ASC
application_username = 'user_name' # Should be the same you use when
                                   # making purchases
nonce = uuid.uuid4()
timestamp = int(round(time.time() * 1000))

payload = '\u2063'.join([bundle_id, 
                         key_id, 
                         product, 
                         offer, 
                         application_username, 
                         str(nonce), # Should be lower case
                         str(timestamp)])

# Read the key file
with open('cert.der', 'rb') as myfile:
  der = myfile.read()

signing_key = SigningKey.from_der(der)

signature = signing_key.sign(payload.encode('utf-8'), 
                             hashfunc=hashlib.sha256, 
                             sigencode=sigencode_der)
encoded_signature = base64.b64encode(signature)

print(str(encoded_signature, 'utf-8'), str(nonce), str(timestamp), key_id)

Это просто доказательство концепции. Вы захотите это на своем сервере и, возможно, будете иметь некоторую логику, чтобы определить, для данного пользователя, является ли запрошенное предложение подходящим.

После того как вы сгенерировали подпись, nonce и timestamp отправляют их вместе с key_id обратно в ваше приложение, где вы можете создать SKPaymentDiscount.

Отказ от ответственности: я работаю в RevenueCat. Мы поддерживаем предложения по подписке с помощью нашего SDK, подпись кода не требуется: https://www.revenuecat.com/2019/04/25/signing-ios-subscription-offers

1 голос
/ 17 апреля 2019

Я могу подтвердить, что это работает:

<?php
use Ramsey\Uuid\Uuid;

class ItunesSignatureGenerator {
    private $appBundleID = 'your.bundle.id';

    private $keyIdentifier = 'ZZZZZZZ';

    private $itunesPrivateKeyPath = '/path/to/the/file.p8;

    /**
     * @see https://developer.apple.com/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers
     *
     * @param $productIdentifier
     * @param $offerIdentifier
     *
     * @return Signature
     */
    public function generateSubscriptionOfferSignature($productIdentifier, $offerIdentifier)
    {
        $nonce = strtolower(Uuid::uuid1()->toString());
        $timestamp = time() * 1000;
        $applicationUsername = 'username';

        $message = implode(
            "\u{2063}",
            [
                $this->appBundleID,
                $this->keyIdentifier,
                $productIdentifier,
                $offerIdentifier,
                $applicationUsername,
                $nonce,
                $timestamp
            ]
        );

        $message = $this->sign($message);

        return new Signature(
            base64_encode($message),
            $nonce,
            $timestamp,
            $this->keyIdentifier
        );
    }

    private function sign($data)
    {
        $signature = '';

        openssl_sign(
            $data,
            $signature,
            openssl_get_privatekey('file://' . $this->itunesPrivateKeyPath),
            OPENSSL_ALGO_SHA256
        );

        return $signature;
    }
}

У нас возникла проблема на стороне клиента, поскольку устройство было подключено к двум различным учетным записям itunes, одной обычной и одной изолированной программной среде.Это создавало неверную ошибку подписи, которая не имела смысла.Мы отключаем обычную учетную запись и просто используем учетную запись песочницы, и все работает.

0 голосов
/ 07 мая 2019

Раньше у меня были проблемы с предложениями подписки, но эта проблема на GitHub помогла мне заставить его работать.Я установил Sop CryptoBridge библиотеку (composer require sop/crypto-bridge), и она, наконец, сработала для моего клиента приложения для iOS.Вот мой рабочий код PHP:

use Sop\CryptoBridge\Crypto;
use Sop\CryptoEncoding\PEM;
use Sop\CryptoTypes\AlgorithmIdentifier\Hash\SHA256AlgorithmIdentifier;
use Sop\CryptoTypes\AlgorithmIdentifier\Signature\SignatureAlgorithmIdentifierFactory;
use Sop\CryptoTypes\Asymmetric\PrivateKeyInfo;

// you can copy your p8 file contents here or just use file_get_contents()
$privateKeyPem = <<<'PEM'
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
PEM;

// load private key
$privateKeyInfo = PrivateKeyInfo::fromPEM(PEM::fromString($privateKeyPem));
// you can also load p8 file like this
// PrivateKeyInfo::fromPEM(PEM::fromFile($pathToP8));

// combine the parameters
$appBundleId = 'com.github.yaronius'; // iOS app bundle ID
$keyIdentifier = 'A1B2C3D4E5'; // Key ID from AppStore Connect
$productIdentifier = 'product_identifier';
$offerIdentifier = 'offer_identifier';
$applicationUsername = 'username'; // same as in the iOS app
$nonce = 'db6ba7a6-9ec2-4504-bcb9-c0dfbdc3d051'; // some UUID in lowercase
$timestamp = time() * 1000; // milliseconds! as stated in the docs

$dataArray = [
    $appBundleId,
    $keyIdentifier,
    $productIdentifier,
    $offerIdentifier,
    $applicationUsername,
    $nonce,
    $timestamp
];
$data = implode("\u{2063}", $dataArray);

// signature algorithm
$algo = SignatureAlgorithmIdentifierFactory::algoForAsymmetricCrypto(
    $privateKeyInfo->algorithmIdentifier(),
    new SHA256AlgorithmIdentifier()
);
// generate signature
$signature = Crypto::getDefault()->sign($data, $privateKeyInfo, $algo);

// encode as base64 encoded DER
$encodedSignature = base64_encode($signature->toDER());
// send signature to your app
echo $encodedSignature;

Имейте в виду несколько вещей:

  • в качестве разделителя вы должны использовать код Unicode PHP , т.е."\u{2063}".Использование '\u2063' у меня не сработало.
  • $nonce в нижнем регистре UUID
  • $timestamp в миллисекундах (то есть time() * 1000).

И это должно работать как шарм.

0 голосов
/ 17 апреля 2019

Я пытаюсь создать что-то в php, но это кажется непрактичным, может быть, что-то определить в кодировке?

<?php

class Key_offer {

    var $appBundleId;
    var $keyIdentifier;
    var $productIdentifier;
    var $offerIdentifier;
    var $applicationUsername;
    var $nonce;
    var $timestamp;

    function __construct() {
        // Setting Data
        $this->appBundleId = 'bundle-app-id';
        $this->keyIdentifier = '0123456789';
        $this->productIdentifier = $_POST["productIdentifier"] ?? "";
        $this->offerIdentifier = $_POST["offerIdentifier"] ?? "";
        $this->applicationUsername = $_POST["usernameHash"] ?? "";  // usare lo stesso anche nella chiamata che si effettua da Xcode
        $this->nonce = strtolower( $this->gen_uuid() ); // genera UUID formato 4;
        $this->timestamp = time(); // get timeStump
    }

    function rsa_sign($policy, $private_key_filename) {
        $signature = "";
        // load the private key
        $fp = fopen($private_key_filename, "r");
        $priv_key = fread($fp, 8192);
        fclose($fp);
        $pkeyid = openssl_get_privatekey($priv_key);
        // compute signature
        openssl_sign($policy, $signature, $pkeyid);
        // free the key from memory
        openssl_free_key($pkeyid);
        return $signature;
     }

     function gen_uuid() {
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            // 32 bits for "time_low"
            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
            // 16 bits for "time_mid"
            mt_rand( 0, 0xffff ),
            // 16 bits for "time_hi_and_version",
            // four most significant bits holds version number 4
            mt_rand( 0, 0x0fff ) | 0x4000,
            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            mt_rand( 0, 0x3fff ) | 0x8000,
            // 48 bits for "node"
            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
        );
    }

    function get() {
        $text = utf8_encode($this->appBundleId.'\u2063'.$this->keyIdentifier.'\u2063'.$this->productIdentifier.'\u2063'.$this->offerIdentifier.'\u2063'.$this->applicationUsername.'\u2063'.$this->nonce.'\u2063'.$this->timestamp);

        $signature0 = $this->rsa_sign($text, "key.pem"); // SubscriptionKey_43PF4FTV2X.p8
        $signature = hash('sha256', $signature0);
        $array = array(
            'lowUUid' => $this->nonce,
            'timeStump' => $this->timestamp,
            'identifier' => $this->offerIdentifier,
            'keyid' => $this->keyIdentifier,
            'signature' => base64_encode($signature)
        );

        return json_encode($array);
    }


}

$obj = new Key_offer();
echo $obj->get();

?>
0 голосов
/ 01 апреля 2019

Result

Я нашел этот пример в Интернете, но, к сожалению, результат не является положительным. Пример учебника

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