Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

244
Views
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

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

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

over 4 years ago · Santiago Trujillo Report

0

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.

over 4 years ago · Santiago Trujillo Report

0

I used to have problems with subscription offers, but this issue on GitHub helped me to make it work. I installed Sop CryptoBridge library (composer require sop/crypto-bridge) and it finally worked for my iOS app client. Here is my working PHP code:

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;

Keep in mind a few things:

  • as a delimiter you have to use PHP Unicode codepoint, i.e. "\u{2063}". Using '\u2063' did not work for me.
  • $nonce is lowercase UUID
  • $timestamp is in milliseconds (i.e. time() * 1000).

And it should work like a charm.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!