I am efficiently utilizing Apple subscriptions in my app, however I am encountering SKErrorCodeDomain error 18 when attempting to use a subscription supply.
I would like apply supply code first time just for subscription.
I’ve tried many answer from on-line search however difficulty will not be fastened.
Under are particulars of what i set in appstore and what i’ve examined.
Subscription Provide Particulars
- Provide Sort: For the primary month
- Buyer Eligibility: New, Current, and Expired Subscribers
- Code Standing: Energetic
Provide Code Creation Steps:
- App Retailer Join → App → Subscription → Choose Subscription Product → Provide Codes → Add → Add Customized Codes
Signature Era for Promotional Gives
I am following Apple’s documentation to generate a signature:
https://developer.apple.com/documentation/storekit/generating-a-signature-for-promotional-offers
I’ve constructed the payload as instructed:
appBundleId + 'u2063' + keyIdentifier + 'u2063' + productIdentifier + 'u2063' + offerIdentifier + 'u2063' + appAccountToken + 'u2063' + nonce + 'u2063' + timestamp
Keys and Identifiers
keyIdentifier
, issuerId
, and .p8
file are obtained from:
- App Retailer Join → Customers and Entry → Integrations → In-App Buy
- Check person created below:
- App Retailer Join → Customers and Entry → Sandbox → Check Accounts
- Logged in with this account on the iPhone
What I’ve Tried
Apple’s pattern code to generate a signature
Downloaded from
const specific = require('specific');
const router = specific.Router();
const crypto = require('crypto');
const ECKey = require('ec-key');
const secp256k1 = require('secp256k1');
const uuidv4 = require('uuid/v4');
const KeyEncoder = require('key-encoder');
const keyEncoder = new KeyEncoder('secp256k1');
const fs = require('fs');
perform getKeyID() {
return "KEYIDXXXXX";
}
router.put up('/supply', perform(req, res) {
const appBundleID = req.physique.appBundleID;
const productIdentifier = req.physique.productIdentifier;
const subscriptionOfferID = req.physique.offerID;
const applicationUsername = req.physique.applicationUsername;
const nonce = uuidv4();
const currentDate = new Date();
const timestamp = currentDate.getTime();
const keyID = getKeyID();
const payload = appBundleID + 'u2063' +
keyID + 'u2063' +
productIdentifier + 'u2063' +
subscriptionOfferID + 'u2063' +
applicationUsername + 'u2063'+
nonce + 'u2063' +
timestamp;
// Get the PEM-formatted non-public key string related to the Key ID.
// const keyString = getKeyStringForID(keyID);
// Learn the .p8 file
const keyString = fs.readFileSync('./SubscriptionKey_KEYIDXXXXX.p8', 'utf8');
// Create an Elliptic Curve Digital Signature Algorithm (ECDSA) object utilizing the non-public key.
const key = new ECKey(keyString, 'pem');
// Arrange the cryptographic format used to signal the important thing with the SHA-256 hashing algorithm.
const cryptoSign = key.createSign('SHA256');
// Add the payload string to signal.
cryptoSign.replace(payload);
/*
The Node.js crypto library creates a DER-formatted binary worth signature,
after which base-64 encodes it to create the string that you'll use in StoreKit.
*/
const signature = cryptoSign.signal('base64');
/*
Verify that the signature passes verification by utilizing the ec-key library.
The verification course of is just like creating the signature, besides it makes use of 'createVerify'
as a substitute of 'createSign', and after updating it with the payload, it makes use of `confirm` to cross in
the signature and encoding, as a substitute of `signal` to get the signature.
This step will not be required, but it surely's helpful to examine when implementing your signature code.
This helps debug points with signing earlier than sending transactions to Apple.
If verification succeeds, the subsequent really useful testing step is trying a purchase order
within the Sandbox atmosphere.
*/
const verificationResult = key.createVerify('SHA256').replace(payload).confirm(signature, 'base64');
console.log("Verification outcome: " + verificationResult)
// Ship the response.
res.setHeader('Content material-Sort', 'utility/json');
res.json({ 'keyID': keyID, 'nonce': nonce, 'timestamp': timestamp, 'signature': signature });
});
module.exports = router;
Postman request and response
Request URL: http://192.168.1.141:3004/supply
Request JSON: {
"appBundleID":"com.app.bundleid",
"productIdentifier":"subscription.product.id",
"offerID":"OFFERCODE1",
"applicationUsername":"01234b43791ea309a1c3003412bcdaaa09d39a615c379cc246f5f479760629a1"
}
Response JSON: {
"keyID": "KEYIDXXXXX",
"nonce": "f98f2cda-c7a6-492f-9f92-e24a6122c0c9",
"timestamp": 1753510571664,
"signature": "MEYCIQCnA8UGWhTiCF+F6S55Zl6hpjnm7SC3aAgvmTBmQDnsAgIhAP6xIeRuREyxxx69Ve/qjnONq7pF1cK8TDn82fyePcqz"
}
Xcode Code
func purchase(_ product: SKProduct) {
let discountOffer = SKPaymentDiscount(
identifier: "OFFERCODE1",
keyIdentifier: "KEYIDXXXXX",
nonce: UUID(uuidString: "f98f2cda-c7a6-492f-9f92-e24a6122c0c9")!,
signature: "MEYCIQCnA8UGWhTiCF+F6S55Zl6hpjnm7SC3aAgvmTBmQDnsAgIhAP6xIeRuREyxxx69Ve/qjnONq7pF1cK8TDn82fyePcqz",
timestamp: 1753510571664)
let fee = SKMutablePayment(product: product)
fee.applicationUsername = "01234b43791ea309a1c3003412bcdaaa09d39a615c379cc246f5f479760629a1"
fee.paymentDiscount = discountOffer
SKPaymentQueue.default().add(fee)
}
Difficulty
Even following directions to the documentation and trying numerous mixtures, the supply retains failing with SKErrorCodeDomain error 18.
Has anybody else skilled this? Any strategies as to what could also be amiss or how it may be corrected?