Subscription Offer generate signature in PHP

is I am trying to do this https://developer.apple.com/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers in PHP.
I don't have any error on the generation of the signature but on the client side we get a "SKErrorInvalidSignature" error.
I am wondering if I did the signature generation properly. It's really specific to PHP but maybe someone can help:


public function generateSubscriptionOfferSignature($productIdentifier, $offerIdentifier)
{
  $nonce = strtolower(Uuid::uuid1()->toString());
  $timestamp = time() * 1000;
  $applicationUsername = '';

  $separator = json_decode('"\u2063"');

  $unsignedSignature = implode(
  $separator,
  [
  $this->appBundleID,
  $this->keyIdentifier,
  $productIdentifier,
  $offerIdentifier,
  $applicationUsername,
  $nonce,
  $timestamp
  ]
  );

  $signature = null;

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

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


All the variable have been triple check and durint the implementation I got error on the openssl_sign and the openssl_get_privatekey now they do work.
The only 2 things I can spot that could be a problem is:
A.
$separator = json_decode('"\u2063"'); Does not return the expected character.


B.

openssl_sign OPENSSL_ALGO_SHA256 does not qualify for (from the documentation):

Sign the Combined String

Sign the combined UTF-8 string with the following key and algorithm:

  • The PKCS#8 private key (downloaded from App Store Connect) that corresponds to the
    keyIdentifier
    in the UTF-8 string.
  • The Elliptic Curve Digital Signature Algorithm (ECDSA) with a SHA-256 hash.


But maybe I miss something else ?

Replies

Sorry if I missed it (A&B maybe?) - pls. show the full/exact sig related error(s), thanks.

And here is simplified client code:


struct SubscriptionOfferSignature: Codable {
    let encodedSignature: String
    let nonce: String
    let timestamp: Int
    let keyIdentifier: String
}

func prepareOffer(for product: SKProduct, completion: @escaping (Result) -> ()) {
    guard let discountIdentifier = product.discounts.first?.identifier else {
        completion(.failure(PurchaseError.missingDiscount))
        return
    }
    service.generateOfferSignatureFor(productIdentifier: product.productIdentifier, offerIdentifier: discountIdentifier) { [weak self] result in
        guard let strongSelf = self else { return }
        switch result {
        case .success(let subscriptionOfferSignature):
            let nonceUUID = UUID(uuidString: subcriptionOfferSignature.nonce)!
            let discountOffer = SKPaymentDiscount(identifier: discountIdentifier, keyIdentifier: subcriptionOfferSignature.keyIdentifier, nonce: nonceUUID, signature: subcriptionOfferSignature.encodedSignature, timestamp: NSNumber(value: subcriptionOfferSignature.timestamp))
            completion(.success(discountOffer))
        case .failure(let error):
            print(error)
            completion(.failure(error))
        }
    }
}

func purchase(_ product: SKProduct, with discountOffer: SKPaymentDiscount) {
    let payment = SKMutablePayment(product: product)
    payment.applicationUsername = ""
    payment.paymentDiscount = discountOffer
    SKPaymentQueue.default().add(payment)
}


And when I get notified by

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])

transaction's state is failed with "Error Domain=SKErrorDomain Code=12 "Cannot connect to iTunes Store" UserInfo={NSLocalizedDescription=Cannot connect to iTunes Store}"

I can see that paymentDiscount object on transaction's payment property has all the values I've set before (identifier, keyIdentifier, nonce, signature, timestamp). So not sure where else to look at.

"Error Domain=SKErrorDomain Code=12 "Cannot connect to iTunes Store" UserInfo={NSLocalizedDescription=Cannot connect to iTunes Store}"


Focusing just on that error alone, is there anything in the SO thread that pertains?


https://stackoverflow.com/questions/18891548/skerrordomain-code-0-cannot-connect-to-itunes-store> "Error Domain=SKErrorDomain Code=12 "Cannot connect to iTunes Store" UserInfo={NSLocalizedDescription=Cannot connect to iTunes Store}"