Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a Signature for Subscription Offers - Xcode - Swift

I wanted to ask if someone has already implemented the new Offers for the inapp-subscription (auto renewal), the difficulty in creating server-side the system to create this signature using the p8 key with php if possible. I found this on the Apple documentation, I'm not sure understanding it: https://developer.apple.com/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers

like image 354
mfiore Avatar asked Mar 29 '19 09:03

mfiore


2 Answers

Here's a walkthrough from RevenueCat: iOS Subscription Offers

The post contains much more detail, but the signature generation is:

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)

This is just a proof of concept. You will want this on your server and perhaps have some logic to determine, for a given user, if the requested offer is appropriate.

Once you’ve generated the signature, nonce and timestamp send these along with the key_id back to your app where you can create an SKPaymentDiscount.

Disclaimer: I work at RevenueCat. We support Subscription Offers out of the box with our SDK, no code-signing required: https://www.revenuecat.com/2019/04/25/signing-ios-subscription-offers

like image 157
enc_life Avatar answered Oct 22 '22 20:10

enc_life


I can confirm that this is working:

<?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;
    }
}

We had a issue on the client side because the device was connected on 2 different itunes account, one regular and one sandbox. It was creating a invalid signature error that didn't make sens. We disconnect the regular account and just use the sandbox account and everything is working.

like image 25
Martin Poirier Théorêt Avatar answered Oct 22 '22 21:10

Martin Poirier Théorêt