8 Replies
      Latest reply on Dec 3, 2019 8:08 PM by _jeremy
      mpoiriert Level 1 Level 1 (0 points)

        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 ?

        • Re: Subscription Offer generate signature in PHP
          KMT Level 9 Level 9 (15,305 points)

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

          • Re: Subscription Offer generate signature in PHP
            Russ Level 1 Level 1 (0 points)

            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.