1 Reply
      Latest reply on Jul 12, 2019 6:34 PM by ryaz
      MarcelloFiore 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 am wondering if I did the signature generation properly. It's really specific to PHP but maybe someone can help:

         

        class Key_offer {
        
          var $appBundleId;
          var $keyIdentifier;
          var $productIdentifier;
          var $offerIdentifier;
          var $applicationUsername;
          var $nonce;
          var $timestamp;
        
          function __construct() {
          // Setting Data
          $this->appBundleId = 'bundle_id';
          $this->keyIdentifier = 'XXXXXXXXXX';
          $this->productIdentifier = $_POST["productIdentifier"];
          $this->offerIdentifier = $_POST["offerIdentifier"];
          $this->applicationUsername = $_POST["usernameHash"];
          $this->nonce = strtolower( $this->gen_uuid() );
          $this->timestamp = time() * 1000;
          }
        
          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");
          $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();
        
        ?>
        
        

         

         

         

        And in the app (Swift) preparing Offer =>

         

        public func prepareOffer(usernameHash: String, productIdentifier: String, offerIdentifier: String, completion: @escaping (SKPaymentDiscount?) -> Void) {
                let config = URLSessionConfiguration.default
        
        
                let session = URLSession.init(configuration: config) // configuration session
                let url = URL(string: "http://192.168.2.100/Key_Offer/genera_class.php")!
                var request = URLRequest(url: url)
                request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
                request.httpMethod = "POST"
                let postString = "usernameHash=\(usernameHash)&productIdentifier=\(productIdentifier)&offerIdentifier=\(offerIdentifier)"
                request.httpBody = postString.data(using: .utf8)
                let task = session.dataTask(with: request) { (data, response, error) in
                    guard let data = data, error == nil else {                                                 // check for fundamental networking error
                        print("Send POST error= ", error ?? 1)
                        DispatchQueue.main.async {
                            completion(nil)
                        }
                        return
                    }
                    if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {           // check for http errors
                        print("statusCode should be 200, but is \(httpStatus.statusCode)")
                        print("Response = \(String(describing: response))")
                        DispatchQueue.main.async {
                            completion(nil)
                        }
                    }
                    // DA DATA TO JSON CONVERSION
                    let decoder = JSONDecoder.init()
                    do {
                        let post_response = try decoder.decode(SkProd.self, from: data)
                        print(post_response)
                        DispatchQueue.main.async {
                            let lowUUid = UUID.init(uuidString: post_response.lowUUid)!
                            let timeStump = NSNumber.init(value: post_response.timeStump)
                            let identifier = post_response.identifier
                            let keyID = post_response.keyid
                            let signature = post_response.signature
                            let paymentDiscount = SKPaymentDiscount(identifier: identifier, keyIdentifier: keyID, nonce: lowUUid, signature: signature, timestamp: timeStump) // genera la firma per poter creare l'offerta
                            // completed with Success, return a SKPaymentDiscount
                            completion(paymentDiscount)
                        }
        
        
                    } catch let error {
                        print("Errore nella conversione del json API Login", error)
                        DispatchQueue.main.async {
                            completion(nil)
                        }
                    }
                }
                task.resume()
            }

         

        and this is the Purchase function to purchase the offer =>

         

        publicfunc buyProduct(product: SKProduct, usernameHash: String, discountOffer: SKProductDiscount) {
        let payment = SKMutablePayment.init(product: product)
                payment.applicationUsername = usernameHash 
        // Add the offer to the payment.
                print(usernameHash)
                print(product.productIdentifier)
                print(discountOffer.identifier!)
        
        // REQUEST signature from Server
        self.prepareOffer(usernameHash:usernameHash, productIdentifier: product.productIdentifier, offerIdentifier: discountOffer.identifier!) { (prodottoDiscount) in
        if(prodottoDiscount != nil) {
                       // set PaymentDiscount Product
                        payment.paymentDiscount = prodottoDiscount!
        // Add the payment to the queue for purchase.
                        SKPaymentQueue.default().add(payment)
                    }
                }
        
        }

         

        And this is the mistake I get, I hope someone can help me find the error.

        https://www.tuunes.org/screen.png