react-native-iap
react-native-iap copied to clipboard
[iOS] AppAccountToken for App Store Server Notifications
Hello,
iOS has a new JWSTransactionDecodedPayload which contains the following property:
appAccountToken: A UUID that associates the transaction with a user on your own service. If your app doesnโt provide an appAccountToken, this string is empty. For more information, see appAccountToken(_:).
Linked here
I would like to ask you if we can add the appAcountToken property when we purchase and get it from "App Store Server Notifications"
Thank you for all the hard work :).
We have the similar requirement as this.
We also need to send the a key that will associate the transaction as part of the product purchase option as app account token.
Product.PurchaseOption.appAccountToken(UUID)
Not sure if this is already in the roadmap and when could it probably be available?
Thanks.
Hello
We are also facing this issue in our products, it would be a a lot easier if we can set a user identifier token for IOS purchases the way it is feasible for Android
We were wondering if there is any update regarding this feature, and if it will be added anytime soon @hyochan
Thanks for all your efforts :)
Likewise - would be awesome to have this capability added!
CC: @hyochan
Also +1'ing this.
As I understand this would be blocked by https://github.com/dooboolab/react-native-iap/issues/1403 ?
As I understand it, this would not be blocked by migrating to StoreKit v2.
In the original implementation of StoreKit there is a field in the SKMutablePayment
object that allows you to tag a payment to a specific user with an arbitrary string.
If that string happens to be a UUID
, then the receipt will include the appAccountToken
field with that UUID
value.
https://developer.apple.com/documentation/storekit/skmutablepayment/1506088-applicationusername
This would make applicationUserName
field analogous to appAccountToken
.
As I understand it, this would not be blocked by migrating to StoreKit v2.
In the original implementation of StoreKit there is a field in the
SKMutablePayment
object that allows you to tag a payment to a specific user with an arbitrary string.If that string happens to be a
UUID
, then the receipt will include theappAccountToken
field with thatUUID
value.https://developer.apple.com/documentation/storekit/skmutablepayment/1506088-applicationusername
This would make
applicationUserName
field analogous toappAccountToken
.
thanks for pointing this out... we added the applicationUsername parameter to the request subscription function and patched the package... works like a charm ๐๐ฟ
Hi @Wanes-Tutunjian, would you mind posting what you changed and how etc? Would love to do this myself. Thanks
@He1nr1chK modify these files accordingly: RNIapIos.swift
@objc public func buyProduct(
_ sku:String,
appUserNameIOS:String, //Add this line
andDangerouslyFinishTransactionAutomatically: Bool,
resolve: @escaping RCTPromiseResolveBlock = { _ in },
reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
) {
pendingTransactionWithAutoFinish = andDangerouslyFinishTransactionAutomatically
var product: SKProduct?
let lockQueue = DispatchQueue(label: "validProducts")
lockQueue.sync {
for p in validProducts {
if sku == p.productIdentifier {
product = p
break
}
}
}
if let prod = product {
addPromise(forKey: prod.productIdentifier, resolve: resolve, reject: reject)
let payment = SKMutablePayment(product: prod)
payment.applicationUsername = appUserNameIOS //Add this line
SKPaymentQueue.default().add(payment)
} else{
if hasListeners {
let err = [
"debugMessage" : "Invalid product ID.",
"code" : "E_DEVELOPER_ERROR",
"message" : "Invalid product ID.",
"productId" : sku
]
sendEvent(withName: "purchase-error", body: err)
}
reject("E_DEVELOPER_ERROR", "Invalid product ID.", nil)
}
}
==============================================================
RNIapIos.m
RCT_EXTERN_METHOD(buyProduct:
(NSString*)sku
appUserNameIOS:(NSString*)appUserNameIOS
andDangerouslyFinishTransactionAutomatically:(BOOL)andDangerouslyFinishTransactionAutomatically
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
==============================================================
iap.d.ts, this file is under src folder
/**
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
* @param {string} [sku] The product's sku/ID
* @param {string} [appUserNameIOS] The purchaser's user ID
* @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
* @param {string} [purchaseTokenAndroid] purchaseToken that the user is upgrading or downgrading from (Android).
* @param {ProrationModesAndroid} [prorationModeAndroid] UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
* @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
* @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
* @returns {Promise<SubscriptionPurchase | null>} Promise resolves to null when using proratioModesAndroid=DEFERRED, and to a SubscriptionPurchase otherwise
*/
export declare const requestSubscription: (sku: string, appUserNameIOS?: string | undefined, andDangerouslyFinishTransactionAutomaticallyIOS?: boolean, purchaseTokenAndroid?: string | undefined, prorationModeAndroid?: ProrationModesAndroid, obfuscatedAccountIdAndroid?: string | undefined, obfuscatedProfileIdAndroid?: string | undefined) => Promise<SubscriptionPurchase | null>;
==============================================================
iap.js, this file is also under src folder as well
/**
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
* @param {string} [sku] The product's sku/ID
* @param {string} [appUserNameIOS] The purchaser's user ID
* @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
* @param {string} [purchaseTokenAndroid] purchaseToken that the user is upgrading or downgrading from (Android).
* @param {ProrationModesAndroid} [prorationModeAndroid] UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
* @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
* @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
* @returns {Promise<SubscriptionPurchase | null>} Promise resolves to null when using proratioModesAndroid=DEFERRED, and to a SubscriptionPurchase otherwise
*/
export var requestSubscription = function (sku, appUserNameIOS, andDangerouslyFinishTransactionAutomaticallyIOS, purchaseTokenAndroid, prorationModeAndroid, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid) {
if (andDangerouslyFinishTransactionAutomaticallyIOS === void 0) { andDangerouslyFinishTransactionAutomaticallyIOS = false; }
if (purchaseTokenAndroid === void 0) { purchaseTokenAndroid = undefined; }
if (prorationModeAndroid === void 0) { prorationModeAndroid = -1; }
if (obfuscatedAccountIdAndroid === void 0) { obfuscatedAccountIdAndroid = undefined; }
if (obfuscatedProfileIdAndroid === void 0) { obfuscatedProfileIdAndroid = undefined; }
return (Platform.select({
ios: function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
// eslint-disable-next-line no-console
console.warn(
// eslint-disable-next-line max-len
'You are dangerously allowing react-native-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.');
}
return [2 /*return*/, getIosModule().buyProduct(sku, appUserNameIOS, andDangerouslyFinishTransactionAutomaticallyIOS)];
});
}); },
android: function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, sku, purchaseTokenAndroid, prorationModeAndroid, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid)];
});
}); },
}) || Promise.resolve)();
};
Use it like this: requestSubscription(sku, uuidString);
Please make sure that the applicationUserName input must be in UUID format (i.e. 123e4567-e89b-12d3-a456-426614174000), if not it would not be shown in the App Store Server notification under appAccountToken field.
A little more info here, when the subscription is renew from a expired subscription via App Store Manage Subscription page, the uuid you provided previously would not be included.
Hi @jeffy2007 thanks a lot. Really appreciate that you took the time to help out.
As I understand it, this would not be blocked by migrating to StoreKit v2. In the original implementation of StoreKit there is a field in the
SKMutablePayment
object that allows you to tag a payment to a specific user with an arbitrary string. If that string happens to be aUUID
, then the receipt will include theappAccountToken
field with thatUUID
value. https://developer.apple.com/documentation/storekit/skmutablepayment/1506088-applicationusername This would makeapplicationUserName
field analogous toappAccountToken
.thanks for pointing this out... we added the applicationUsername parameter to the request subscription function and patched the package... works like a charm ๐๐ฟ
from which version of RNIap is the applicationUsername included in the subscription function?
As I understand it, this would not be blocked by migrating to StoreKit v2. In the original implementation of StoreKit there is a field in the
SKMutablePayment
object that allows you to tag a payment to a specific user with an arbitrary string. If that string happens to be aUUID
, then the receipt will include theappAccountToken
field with thatUUID
value. https://developer.apple.com/documentation/storekit/skmutablepayment/1506088-applicationusername This would makeapplicationUserName
field analogous toappAccountToken
.thanks for pointing this out... we added the applicationUsername parameter to the request subscription function and patched the package... works like a charm ๐๐ฟ
from which version of RNIap is the applicationUsername included in the subscription function?
Seems to have been resolved in https://github.com/dooboolab/react-native-iap/pull/1763 which was released in 8.4.0: https://github.com/dooboolab/react-native-iap/releases/tag/8.4.0
Hoping to upgrade and test these changes over the next couple weeks :D
Please help us test 11.0.0-alpha1 (Migration to Storekit 2) Otherwise this has been merged on main. Can be found on the latest 10.x.x