I am still using App Store Server Notifications 1.0, i wanna to recognized the situation that user didn't fix while pay issue occur, so he is expired after grace period.
But I notice that there will be a long time after his grace period I received the notification.
Such as:
{
"auto_renew_product_id": "com.protect.adpatrol.weekly2",
"auto_renew_status": "false",
"auto_renew_status_change_date": "2023-01-03 16:50:59 Etc/GMT",
"auto_renew_status_change_date_ms": "1672764659000",
"auto_renew_status_change_date_pst": "2023-01-03 08:50:59 America/Los_Angeles",
"environment": "PROD",
"notification_type": "DID_CHANGE_RENEWAL_STATUS",
"original_transaction_id": 150001223579830,
"unified_receipt": {
"environment": "Production",
"latest_receipt_info": [
{
"expires_date": "2022-11-01 09:15:38 Etc/GMT",
"expires_date_ms": "1667294138000",
"expires_date_pst": "2022-11-01 02:15:38 America/Los_Angeles",
"in_app_ownership_type": "PURCHASED",
"is_in_intro_offer_period": "false",
"is_trial_period": "true",
"original_purchase_date": "2022-10-29 09:15:42 Etc/GMT",
"original_purchase_date_ms": "1667034942000",
"original_purchase_date_pst": "2022-10-29 02:15:42 America/Los_Angeles",
"original_transaction_id": "150001223579830",
"product_id": "com.protect.adpatrol.weekly2",
"purchase_date": "2022-10-29 09:15:38 Etc/GMT",
"purchase_date_ms": "1667034938000",
"purchase_date_pst": "2022-10-29 02:15:38 America/Los_Angeles",
"quantity": "1",
"subscription_group_identifier": "20900376",
"transaction_id": "150001223579830",
"web_order_line_item_id": "150000550680992"
}
],
"pending_renewal_info": [
{
"auto_renew_product_id": "com.protect.adpatrol.weekly2",
"auto_renew_status": "0",
"expiration_intent": "2",
"grace_period_expires_date": "2022-11-07 10:15:38 Etc/GMT",
"grace_period_expires_date_ms": "1667816138000",
"grace_period_expires_date_pst": "2022-11-07 02:15:38 America/Los_Angeles",
"is_in_billing_retry_period": "0",
"original_transaction_id": "150001223579830",
"product_id": "com.protect.adpatrol.weekly2"
}
],
"status": 0
}
}
Notice that his grace period is expire at '2022-11-07 10:15:38 Etc/GMT', but his renew status change at '2023-01-03 16:50:59 Etc/GMT'. It is almost 60 days.
So this message is sent after billing retry attempts(up to 60 days) fail?
Post
Replies
Boosts
Views
Activity
I can receive the email for inviting me to join development team, and after I accept invitation, I was granted as the admin
And then I invited myself to join the Internal Testing of TestFlight. But unfortunately whatever how many times I try to Resend Invite, I couldn't receive any email.
Does anyone can help me with that?
It is in sandbox environment, and our serice is using v1
Base on this doc, the INTERACTIVE_RENEWAL notification would be sent:
Subscription is active; customer upgraded to another SKU
Subscription has expired; customer resubscribed to another SKU (upgrade or downgrade)
{
"environment": "Sandbox",
"receipt": {
"receipt_type": "ProductionSandbox",
"bundle_id": "co.appeconomy.alarm",
"receipt_creation_date": "2023-04-26 10:13:24 Etc/GMT",
"request_date": "2023-04-26 10:17:11 Etc/GMT",
"original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
"original_application_version": "1.0"
},
"latest_receipt_info": [
{
"quantity": "1",
"product_id": "co.appeconomy.alarm.monthly",
"transaction_id": "2000000320391970",
"original_transaction_id": "2000000320390725",
"purchase_date": "2023-04-26 10:11:53 Etc/GMT",
"original_purchase_date": "2023-04-26 10:09:59 Etc/GMT",
"expires_date": "2023-04-26 10:16:53 Etc/GMT",
"web_order_line_item_id": "2000000026088214",
"is_trial_period": "false",
"is_in_intro_offer_period": "false",
"in_app_ownership_type": "PURCHASED",
"subscription_group_identifier": "20906883"
},
{
"quantity": "1",
"product_id": "co.appeconomy.alarm.monthly",
"transaction_id": "2000000320390725",
"original_transaction_id": "2000000320390725",
"purchase_date": "2023-04-26 10:09:53 Etc/GMT",
"original_purchase_date": "2023-04-26 10:09:59 Etc/GMT",
"expires_date": "2023-04-26 10:11:53 Etc/GMT",
"web_order_line_item_id": "2000000026088213",
"is_trial_period": "true",
"is_in_intro_offer_period": "false",
"in_app_ownership_type": "PURCHASED",
"subscription_group_identifier": "20906883"
}
],
"pending_renewal_info": [
{
"auto_renew_product_id": "co.appeconomy.alarm.monthly",
"is_in_billing_retry_period": "0",
"product_id": "co.appeconomy.alarm.monthly",
"original_transaction_id": "2000000320390725",
"auto_renew_status": "0"
}
],
"status": 0
}
these is the response from verifyReceipt api
Can notice that I request at '2023-04-26 10:17:11 Etc/GMT' and latest receipt shows that subscription is expired at '2023-04-26 10:16:53 Etc/GMT'. So obviously this subscription is expired already
But when I resubscribed the same sku(in this case is co.appeconomy.alarm.monthly) in the Setting > App Store > Edit Subscription. My server can still receive INTERACTIVE_RENEWAL notification.
{
"bid": "co.appeconomy.alarm",
"bvrs": "2",
"environment": "Sandbox",
"unified_receipt": {
"status": 0,
"environment": "Sandbox",
"latest_receipt_info": [
{
"quantity": "1",
"product_id": "co.appeconomy.alarm.monthly",
"expires_date": "2023-04-26 10:23:47 Etc/GMT",
"purchase_date": "2023-04-26 10:18:47 Etc/GMT",
"transaction_id": "2000000320398679",
"is_trial_period": "false",
"in_app_ownership_type": "PURCHASED",
"web_order_line_item_id": "2000000026088350",
"original_transaction_id": "2000000320390725",
"is_in_intro_offer_period": "false",
"subscription_group_identifier": "20906883"
},
{
"quantity": "1",
"product_id": "co.appeconomy.alarm.monthly",
"expires_date": "2023-04-26 10:16:53 Etc/GMT",
"purchase_date": "2023-04-26 10:11:53 Etc/GMT",
"transaction_id": "2000000320391970",
"is_trial_period": "false",
"in_app_ownership_type": "PURCHASED",
"web_order_line_item_id": "2000000026088214",
"original_transaction_id": "2000000320390725",
"is_in_intro_offer_period": "false",
"subscription_group_identifier": "20906883"
},
{
"quantity": "1",
"product_id": "co.appeconomy.alarm.monthly",
"expires_date": "2023-04-26 10:11:53 Etc/GMT",
"purchase_date": "2023-04-26 10:09:53 Etc/GMT",
"transaction_id": "2000000320390725",
"is_trial_period": "true",
"in_app_ownership_type": "PURCHASED",
"web_order_line_item_id": "2000000026088213",
"original_transaction_id": "2000000320390725",
"is_in_intro_offer_period": "false",
"subscription_group_identifier": "20906883"
}
],
"pending_renewal_info": [
{
"product_id": "co.appeconomy.alarm.monthly",
"auto_renew_status": "1",
"auto_renew_product_id": "co.appeconomy.alarm.monthly",
"original_transaction_id": "2000000320390725"
}
]
},
"auto_renew_status": "true",
"notification_type": "INTERACTIVE_RENEWAL",
"auto_renew_product_id": "co.appeconomy.alarm.monthly",
"original_transaction_id": 2000000320390725
}
From the product_id and subscription_group_identifier can recognize that I really resubscribed the same sku, so why? Does I miss any exceptional case in document?
assume that I have this 5 PRODUCT
I purchase Weekly, then I switch to Monthly, it is upgrade, will receive INTERACTIVE_RENEWAL, indicate that new plan takes effect immediately.
I purchase Yearly, then I switch to Monthly, it is downgrade, will receive DID_CHANGE_RENEWAL_PREF, indicate that new plan takes effect at the next renewal.
I purchase Quarterly, then I switch to Quarterly2, it is crossgrade, will receive INTERACTIVE_RENEWAL, indicate that new plan takes effect immediately; Because the subscriptions are the same duration.
I purchase Quarterly, then I switch to Monthly, it is crossgrade, will receive DID_CHANGE_RENEWAL_PREF, indicate that new plan takes effect at the next renewal; Because the durations are different.
So, if I receive INTERACTIVE_RENEWAL notification, how can I recognize it is upgrade, or crossgrade to same duration product.
If I receive DID_CHANGE_RENEWAL_PREF, how can I recognized it is downgrade, or crossgrade to different duration product.
Does anyone can help me, thanks so much!! Orz
I turn off the auto renwal, and after the current subscription expired, I call verifyReceipt api to check the latest status.
I just found that
"pending_renewal_info": [
{
"auto_renew_product_id": "co.ringalarm.swtich.monthly",
"is_in_billing_retry_period": "0",
"product_id": "co.ringalarm.swtich.monthly",
"original_transaction_id": "2000000333327838",
"auto_renew_status": "0"
}
],
It is odd cause I thought this field is appear while subscription is into the billing retry.
Does this field can help me to decide whether the subscription is expired?
Cause in sandbox env I notice a receipt like this
{
"environment": "Sandbox",
"unified_receipt": {
"status": 0,
"environment": "Sandbox",
"latest_receipt_info": [
{
"quantity": "1",
"product_id": "3monthplan",
"expires_date": "2023-07-25 04:50:29 Etc/GMT",
"purchase_date": "2023-07-25 04:41:29 Etc/GMT",
"transaction_id": "2000000375156349",
"expires_date_ms": "1690260629000",
"is_trial_period": "false",
"expires_date_pst": "2023-07-24 21:50:29 America/Los_Angeles",
"purchase_date_ms": "1690260089000",
"purchase_date_pst": "2023-07-24 21:41:29 America/Los_Angeles",
"in_app_ownership_type": "PURCHASED",
"original_purchase_date": "2023-07-25 03:20:41 Etc/GMT",
"web_order_line_item_id": "2000000032603282",
"original_transaction_id": "2000000375122102",
"is_in_intro_offer_period": "false",
"original_purchase_date_ms": "1690255241000",
"original_purchase_date_pst": "2023-07-24 20:20:41 America/Los_Angeles",
"subscription_group_identifier": "20572434"
},
....
{
"quantity": "1",
"product_id": "3monthplan",
"expires_date": "2023-07-25 04:03:49 Etc/GMT",
"purchase_date": "2023-07-25 03:54:49 Etc/GMT",
"transaction_id": "2000000375136003",
"expires_date_ms": "1690257829000",
"is_trial_period": "false",
"expires_date_pst": "2023-07-24 21:03:49 America/Los_Angeles",
"purchase_date_ms": "1690257289000",
"purchase_date_pst": "2023-07-24 20:54:49 America/Los_Angeles",
"in_app_ownership_type": "PURCHASED",
"original_purchase_date": "2023-07-25 03:20:41 Etc/GMT",
"web_order_line_item_id": "2000000032600544",
"original_transaction_id": "2000000375122102",
"is_in_intro_offer_period": "false",
"original_purchase_date_ms": "1690255241000",
"original_purchase_date_pst": "2023-07-24 20:20:41 America/Los_Angeles",
"subscription_group_identifier": "20572434"
},
{
"quantity": "1",
"product_id": "monthlyplan",
"expires_date": "2023-07-25 03:38:37 Etc/GMT",
"purchase_date": "2023-07-25 03:35:37 Etc/GMT",
"transaction_id": "2000000375127458",
"expires_date_ms": "1690256317000",
"is_trial_period": "false",
"expires_date_pst": "2023-07-24 20:38:37 America/Los_Angeles",
"purchase_date_ms": "1690256137000",
"cancellation_date": "2023-07-25 04:38:50 Etc/GMT",
"purchase_date_pst": "2023-07-24 20:35:37 America/Los_Angeles",
"cancellation_reason": "0",
"cancellation_date_ms": "1690259930000",
"cancellation_date_pst": "2023-07-24 21:38:50 America/Los_Angeles",
"in_app_ownership_type": "PURCHASED",
"original_purchase_date": "2023-07-25 03:20:41 Etc/GMT",
"web_order_line_item_id": "2000000032600434",
"original_transaction_id": "2000000375122102",
"is_in_intro_offer_period": "false",
"original_purchase_date_ms": "1690255241000",
"original_purchase_date_pst": "2023-07-24 20:20:41 America/Los_Angeles",
"subscription_group_identifier": "20572434"
},
...
]
},
"auto_renew_status": "false",
"cancellation_date": "2023-07-25 04:38:50 Etc/GMT",
"notification_type": "CANCEL",
"cancellation_date_ms": "1690259930000",
"auto_renew_product_id": "3monthplan",
"cancellation_date_pst": "2023-07-24 21:38:50 America/Los_Angeles",
"web_order_line_item_id": "2000000032600434",
"original_transaction_id": 2000000375122102
}
plan is keeping renew.
What if I refund in prod env, and after refund agree, is that my subscription would auto turn off?
Assume user purchase a auto-renewal subscription through apple, and send our server his receipt. What if our server doesn't call apple verifyReceipt endpoint, will this purchase be refunded automaticlly after a while?
And how about consumable, non-consumable sku?
Looking forward to receive your response. Thank you :)
In the old verifyReceipt endpoint doc, there is an important desc
As a best practice, always call the production URL https://buy.itunes.apple.com/verifyReceipt first and proceed to verify with the sandbox URL if you receive a 21007 status code. Following this approach ensures that you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store.
So I can know the transactionId is for sandbox if return back 21007.
And I am about to access to new App Store Server API, so the same, I wonder how can I recognize the transactionId is from sandbox by status code?
The most likely desc is this:
If you don’t have environment information, follow these steps:
Call the endpoint using the production URL. If the call succeeds, the original transaction identifier belongs to the production environment.
If you receive an [errorCode 4040005] with errorMessage as OriginalTransactionIdNotFoundError, (or HTTP response code 404 from the Send Consumption Information endpoint), call the endpoint using the sandbox environment.
If the call succeeds, the original transaction identifier belongs to the sandbox environment. If the call fails with the same error code, the original transaction identifier isn’t present in either environment.
And I have a try for Get Transaction Info API, but actually get 4040010, Transaction id not found. as return.
So I just wanna clear that is there any doc clarify this point that I miss?
Looking forward to your response, sincerely!! :)
I try to call Get Transaction Info from App Store Server API, and the transactionId is for a Non-consumable type product, but it is odd that there are so many different transactionId and they have a same originalTransactionId
{
"bundleId": "${bundleId}",
"environment": "Production",
"inAppOwnershipType": "PURCHASED",
"originalPurchaseDate": 1691220528000,
"originalTransactionId": "${originalTransactionId}",
"productId": "${productId}",
"purchaseDate": 1691220528000,
"quantity": 1,
"signedDate": 1692590989925,
"storefront": "USA",
"storefrontId": "143441",
"transactionId": "${originalTransactionId}",
"transactionReason": "PURCHASE",
"type": "Non-Consumable"
}
the defination of Non-Consumable is can only purchase once for same apple account. But why there would have originalTransactionId?
If my user purchase and get transaction_id: 2000000395609292, and then another user just makeup a same transaction_id to call the App Store Server API
If in the old way, the receipt seems impossiable to makeup, how about now? Is that equally safe as before?
Or is there any way to protect transaction_id.
I am not that good at security, so please forgive me about missing any point. o(╥﹏╥)o
Looking forward to your response, sincerely!! :)
here are some field in JWSTransactionDecodedPayload and JWSRenewalInfoDecodedPayload, miss this kind of desc: This field is present only for xxxxx
Such as
revocationReason in JWSTransactionDecodedPayload, this field is present only when transaction is refunded. And its possible value is 0 and 1, when this field not present, golang would still unmarshal to default 0 for int32 type
isInBillingRetryPeriod in JWSRenewalInfoDecodedPayload, this field is present only when transaction in billing-retry state(relative doc), and because this field type is boolean, so possible value is true and false, base on the old api desc,
true - The App Store is attempting to renew the subscription.
false - The App Store has stopped attempting to renew the subscription.
When this field not present, golang would still unmarshal to default false for bool type
priceIncreaseStatus in JWSRenewalInfoDecodedPayload, this field is present only when an auto-renewable subscription price increase that requires customer consent. Its possible value is
0 - The customer hasn’t yet responded to an auto-renewable subscription price increase that requires customer consent.
1 - The customer consented to an auto-renewable subscription price increase that requires customer consent, or the App Store has notified the customer of an auto-renewable subscription price increase that doesn’t require consent.
here is original thread
And I got a way to handle it:
By SDK upload introductory_price 😂
introductory_price > 0 that means it is Pay as you go or Pay up front
introductory_price = 0 means it is free trial
So here is another question, is there any api Apple could introduce for backend to query product price?
Looking forward to your response, sincerely!! :)