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