I created a project to validate in-app purchase receipts using c# but I always get error 401, you don't have permission, I followed the instructions exactly but still failed, I created a private key in Certificates, IDs & Profiles and download the .p8 file but it still doesn't work, I also created and tried another key in Users and Access > Integrations > App Store Connect API and also In-App Purchase but it still doesn't work.
When I try to put the created token on jwt.io, I always get an Invalid Signature error
using Jose;
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.IO;
using System.Net;
namespace CheckIAP
{
internal class Program
{
static async Task Main(string[] args)
{
string jwtToken = GenerateAppStoreJwtToken();
string transactionId = "110002159078***";
Console.WriteLine(jwtToken);
string url = $"https://api.storekit.itunes.apple.com/inApps/v2/history/{transactionId}";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
string responseData = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseData);
}
else
{
Console.WriteLine($"Error: {response.StatusCode}, {response.ReasonPhrase}");
string responseData = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseData);
}
}
Console.ReadLine();
}
public static string GenerateAppStoreJwtToken()
{
string teamId = "PZG479xxxx";
string keyId = "JCXH26xxxx";
string issuerId = "7aa0c9c2-***-***-***-xxxx";
string bundleId = "com.***.***";
const string API_KEY = "MIGTAgEAMBMGByqGSM....";
var header = new Dictionary<string, object>()
{
{ "alg", "ES256" },
{ "kid", keyId },
{ "typ", "JWT" }
};
var payload = new Dictionary<string, object>
{
{ "iss", issuerId },
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ "exp", DateTimeOffset.UtcNow.AddMinutes(15).ToUnixTimeSeconds() },
{ "aud", "appstoreconnect-v1" },
{ "sub", bundleId }
};
var key = CngKey.Import(Convert.FromBase64String(API_KEY),
CngKeyBlobFormat.Pkcs8PrivateBlob);
return Jose.JWT.Encode(payload, key, JwsAlgorithm.ES256, header);
}
}
}
App Store Server API
RSS for tagCall this REST API from your server to request and provide information about your customers' in-app purchases.
Posts under App Store Server API tag
87 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi, I'm just using the APPLE api to get the sales, installs and profit data for the month but I don't know why I think I'm not getting all the data because if I look at the financial cut for the month (APRIL FOR EXAMPLE) I see that APPLE it pays me XYZ amount, but in the API, if I get the profit or the sales, neither gives me the correct amount from the financial report. I am doing something wrong? how can i get the correct amount using the APIs
I recently had a request from a Product Owner to implement capability like Netflix has to enable users to switch their payment method from App Store Subscription to Credit Card on our website, as per capability that Netflix has in their account management portal.
We tested it today with a colleague who was paying for Netflix through iOS in-app subscription:
In the Netflix account management pages it showed that he was currently paying via In App Subscription
He updated his payment method to Credit Card in their website's account management portal.
Almost immediately after adding his Credit Card in his iOS Subscription Management settings (we could see the the subscription had been set to no longer renew, with an expiry date)
How is this done - I can't see any API in App Store Server API documentation that gives us a way of cancelling / preventing renewal of subscriptions on behalf of a user... But Netflix can clearly do it somehow...
Hi,
I'm using the App Store Server API for in-app purchase receipt validation. However I received 401 error status code.
My app is ready for submit in App Store Connect, but not yet published the first version.
The receipt is generated using StoreKit test configuration and follow the Sandbox testing instruction. It is generated on a real device using Sandbox Apple account registered in the App Sandbox tester section.
If I go back to use the deprecated verifyReceipt API sandbox endpoint, I get {'status': 21002} error instead.
Is it expected for an App that has not yet published in App Store?
If not, is there any way to test the in-app purchase server-side validation before the App is release?
Do we require a Vietnamese bank account to receive payments from in-app purchases for our new educational app being developed in Japan, or will the funds be automatically transferred to our Japanese bank account?
We've switched over to using the new App Store Server API on our server instead of the deprecated Apple verify receipt method. But we've noticed some differences between our purchase records approved by the App Store Server API and what's shown on the Apple iTunes dashboard for purchases made on May 7th, 2024. Just to clarify, the purchase dates are in UTC time and are taken from the Apple Store API transaction requests. Below are the purchase dates and the subscription names for the 7th.
2024-05-07 22:46:16 - monthly subscription
2024-05-07 22:31:53 - monthly subscription
2024-05-07 22:26:53 - six months subscription
2024-05-07 22:19:16 - monthly subscription
2024-05-07 22:01:04 - monthly subscription
2024-05-07 21:28:56 - six months subscription
2024-05-07 20:09:04 - six months subscription
2024-05-07 20:01:31 - six months subscription
2024-05-07 19:43:24 - monthly subscription
2024-05-07 19:12:30 - annual subscription
2024-05-07 18:30:36 - six months subscription
2024-05-07 00:43:33 - monthly subscription
While our records indicate these transactions, the Apple iTunes dashboard only displays 3 monthly subscriptions for May 7th. May 6th and 5th is worse; almost 1 or 2 monthly subscriptions are showing.
We think there's a problem that Apple engineers or developers need to look into. Before, when we used the verify receipt method in our API, purchases would show up on the iTunes dashboard the next day. But now that we've switched to the new transaction method, there's a delay in seeing these purchases on the dashboard.
Have you migrated to the new App Store Server API and encountered a similar issue? What could be causing this delay?
Hello, I call transaction information for several apps. And I don't have environment information.
That's why I'm calling the sandbox environment as an official guide.
Call the endpoint using the production URL. If the call succeeds, the transaction identifier belongs to the production environment.
If you receive an error code 4040010 TransactionIdNotFoundError, call the endpoint using the sandbox environment.
If the call succeeds, the transaction identifier belongs to the sandbox environment. If the call fails with the same error code, the transaction identifier isn’t present in either environment.
However, unlike the official guide, certain apps sometimes use production URLs to call endpoints to receive 401 status codes and successfully call endpoints using the sandbox environment.
Why does this difference occur unlike the official guide?
I receive a 401 status code in response from a production environment endpoint, I want to know if this status code is a real error or if I have to call it into a sandbox environment.
Can I check with the key 'x-apple-request-uuid' in the response header?
Please let me know the solution.
Hi, I am implementing in-app subscriptions in my application and would like to set up a module in my admin portal by which I can create subscription products via my custom module. For this purpose I need API's that allow me to create subscription products. Please let me know if such a solution is possible, if it is, then please share the API's and payload by which I could implement it.
Thank you in advance.
I call transaction information for several apps. And I don't have environment information.
So I'm calling the sandbox environment as an official guide.
However, unlike the official guide, certain apps sometimes succeed by calling an endpoint using a production URL to receive an error code 401 and calling an endpoint using a sandbox environment.
Why does this difference occur unlike the official guide?
Because of this difference, sometimes it's a production environment and JWT hasn't expired, but I get a 401 error and call the sandbox environment.
2. Please let me know the solution.
Apple automatically refunds the prorated amount on the original subscription after it is upgraded. Since upgrades happen immediately and start a new billing cycle beginning from the day of upgrade, the previous original subscription is refunded for the prorated amount. Is there any way to retrieve the amount that was refunded by Apple?
The transaction that was upgraded is marked with isUpgraded flag to true but the price field on it still shows the full amount and there is no App Store Server Notification that tells us how much was the refund.
How do I get the refunded amount?
I am trying to call Get Transaction Info inApps/v1/transactions/{transactionId}
from PHP server but it always send me back 404 transactions not found,
I receive the transactionId from the app/client when the frontend developer extract the transactionId from the Recipe.
I have generated a good JWT it work with the other endoints such as making some product reads.
I didn't configured server url as I will not use the notification.
Hi,
I am processing both REFUNDED and REVOKED transactions on the client-side and on the server using Apple's App Store Server Notifications (V2).
For transactions with an inAppOwnershipType of FAMILY_SHARED, I receive REVOKE notifications that (sometimes) do not include a revocationDate. What does this imply, and what actions should I take?
Additionally, when I query their transaction IDs using the App Store API, they also lack a revocationDate and do not appear in the results when I filter for revoked transactions.
Is it appropriate to block these users? Why are server-to-server REVOKE notifications sent in the first place if the transaction does not appear as revoked in the API?
Thanks
Hello everyone. I'm try to get out of verifyReceipt endpoint by following this link https://developer.apple.com/videos/play/wwdc2023/10143/
at the end of video so after using ReceiptUtility extract receipt data to get transationId then using getTransactionHistory to do what? it doesn't clear in the video. Where I can get document on how to verify receipt with AppStoreServerAPI.
Hello everyone. I'm try to get out of verifyReceipt for my app. I'm try to following this link https://developer.apple.com/videos/play/wwdc2023/10143/
Then I'm testing by using old receipt from my app. Using receiptUtility.extractTransactionIdFromAppReceipt(appReceipt);
I got this transaction Id 160000542059454. Then follow the step in the video to get all transaction history I realize that it missing transaction which have productype as consumable. If I'm using getTransactionInfo an pass that Id I can fetch the info of that transaction. So my question is how to get all transaction Id history.
Hi,
For a user who subscribed a product via In-App Purchase, how does the application backend know that the user is still subscribed?
The initial purchase happens on the mobile app. Via receiving and validating the receipt of initial purchase, the backend of mobile app would be informed.
However, what is the appropriate option for the subsequent subscription payments?
How do I know whether the user is still paying?
Thanks.
There is the bundleVersion exist in this document, and for the notificationType CONSUMPTION_REQUEST, the payload what we always received contains the bundleVersion, but today we receive one CONSUMPTION_REQUEST request payload without the bundleVersion.
So we wanna know what means of bundleVersion? Is that always exist? when does it not exist?(for both notification type)
Thanks in advance
Hello.
How can I get transaction information when a payment request is submitted to the app store server
I received a version 1 server notification.
auto_renew_status is true and another pending_renewal_info.auto_renew_status is 0. So did the user turn the subscription on or off?
What does this mean and which field I should use to identify user action?
Could anyone help me with that?
The contents are as follows:
{
"environment": "PROD",
"unified_receipt": {
"status": 0,
"environment": "Production",
"latest_receipt_info": [
{
"quantity": "1",
"product_id": "com.protect.adpatrol.weekly2",
"expires_date": "2023-03-19 18:12:56 Etc/GMT",
"purchase_date": "2023-03-12 18:12:56 Etc/GMT",
"transaction_id": "700001139057782",
"expires_date_ms": "1679249576000",
"is_trial_period": "false",
"expires_date_pst": "2023-03-19 11:12:56 America/Los_Angeles",
"purchase_date_ms": "1678644776000",
"purchase_date_pst": "2023-03-12 11:12:56 America/Los_Angeles",
"in_app_ownership_type": "PURCHASED",
"original_purchase_date": "2023-02-28 06:18:59 Etc/GMT",
"web_order_line_item_id": "700000519235393",
"original_transaction_id": "700001128802427",
"is_in_intro_offer_period": "false",
"original_purchase_date_ms": "1677565139000",
"original_purchase_date_pst": "2023-02-27 22:18:59 America/Los_Angeles",
"subscription_group_identifier": "20900376"
},
{
"quantity": "1",
"product_id": "com.protect.adpatrol.weekly2",
"expires_date": "2023-03-03 06:18:57 Etc/GMT",
"purchase_date": "2023-02-28 06:18:57 Etc/GMT",
"transaction_id": "700001128802427",
"expires_date_ms": "1677824337000",
"is_trial_period": "true",
"expires_date_pst": "2023-03-02 22:18:57 America/Los_Angeles",
"purchase_date_ms": "1677565137000",
"purchase_date_pst": "2023-02-27 22:18:57 America/Los_Angeles",
"in_app_ownership_type": "PURCHASED",
"original_purchase_date": "2023-02-28 06:18:59 Etc/GMT",
"web_order_line_item_id": "700000519235392",
"original_transaction_id": "700001128802427",
"is_in_intro_offer_period": "false",
"original_purchase_date_ms": "1677565139000",
"original_purchase_date_pst": "2023-02-27 22:18:59 America/Los_Angeles",
"subscription_group_identifier": "20900376"
}
],
"pending_renewal_info": [
{
"product_id": "com.protect.adpatrol.weekly2",
"auto_renew_status": "0",
"auto_renew_product_id": "com.protect.adpatrol.weekly2",
"original_transaction_id": "700001128802427"
}
]
},
"auto_renew_status": "true",
"notification_type": "DID_CHANGE_RENEWAL_STATUS",
"auto_renew_product_id": "com.protect.adpatrol.weekly2",
"original_transaction_id": 700001128802427,
"auto_renew_status_change_date": "2023-03-12 18:12:58 Etc/GMT",
"auto_renew_status_change_date_ms": "1678644778000",
"auto_renew_status_change_date_pst": "2023-03-12 11:12:58 America/Los_Angeles"
}
Our service has an IOS and Android app and a backend server.
I have a question about handling refunds for simultaneous payments on Android and IOS.
Is there any way to cancel and refund the IOS subscription payment in the backend when Google is paid first and IOS is paid almost simultaneously?
For Android, unlike IOS IAP, the backend server can confirm the purchase with a purchase confirmation (ack), so even if IOS is paid first at almost the same time, it is possible to cancel it by checking the payment data in the backend and not sending an ack to Google's server if the IOS payment is already made.
For V1 used for internal purchase verification, when will the exclusive shared key regenerated after transfer be replaced? Will it affect in-app purchases and subscriptions by online users?
The V2 used for internal purchase verification uses the key ID instead of the dedicated shared key. In this case, what should we pay attention to before and after the transfer? Do I need to regenerate the key ID for the new account? Is the private shared key still useful? Do I need to generate a dedicated shared key again in the transferred App?
What will be the impact on existing subscriptions after the transfer? What do I need to do with the current existing subscriptions?
We have used universalLink, do we need to add a new TeamId to the apple-app-site-assn. txt file?
{
"applinks": {
"apps": [],
"details": [
{
"appID": “TeamIdA.com.***.***”,
"paths": [""]
},
{
"appID": “TeamIdB.com.***.***”,
"paths": [""]
}
]
}
}
We have stored the login information in Keychain Sharing, is there no way to get the original stored information after transfer? Is there a reasonable solution?