Restore undelivered PG purchases
Retrieve PG purchases whose payment is complete but whose product delivery is not finished, and complete the remaining product delivery and transaction finishing.
First complete the setup and installation in Implement PG payments.
When to run
A purchase remains unfinished in the following situations.
- The user finished the payment in the browser but did not return to the app
- Receipt verification succeeded, but product delivery or purchase finalization failed
With PG payments, the app does not receive the payment result directly, so restoration is needed more often than with other markets. Run the restoration when the app launches and when the in-app store screen opens. Even if the two times overlap, do not let the restoration run twice at the same time, and do not process an order while an in-progress purchase is processing the same order.
Overall flow
| Step | Category | What you do |
|---|---|---|
| 1 | Recipe code | Get undelivered purchases |
| 2 | Recipe code | Prepare purchase information |
| 3 | App code, App server, Hive Axyl Server API | Verify receipts and deliver products |
| 4 | Recipe code | Finish the transaction |
| 5 | Hive Axyl SDK | Record delivery results |
1. Get undelivered purchases
- Recipe code Call the recipe code from the app.
Get the list of orders that are not finished yet, and then save all the retrieved items before you process them. If you save them all, you do not lose the remaining items even if processing one item fails.
1.1. Get the list of undelivered purchases
Call FindUndeliveredAsync() to get the list of orders that are not finished yet. If you specify serverId, only the orders for that app server are retrieved; if you omit it, all orders are retrieved.
using Hive.Axyl.Samples.Recipes;
var recipe = new UndeliveredPurchaseRecipe(new PgPurchaseSource());
var query = new UndeliveredQuery(
country: "KR",
language: "ko",
serverId: "server01",
appVersion: Application.version,
accountUuid: accountUuid);
FindUndeliveredOutcome found = await recipe.FindUndeliveredAsync(query, token);
if (found.Status != FindUndeliveredStatus.Success)
{
Debug.LogError($"{found.FailedStep}: {found.BusinessOutcome} {found.Error?.Message}");
return;
}
The recipe performs the following tasks internally.
| Category | Call | Where to check |
|---|---|---|
| Hive Axyl SDK | Get orders with undelivered products with RestorePurchasesAsync() | Get orders with undelivered products |
1.2. Save the retrieved items
Before you process them, save all the retrieved items to pendingStore, the purchase information store described in Implement PG payments, and then process them one at a time, in order, starting from step 2. An empty list means that there are no purchases to finish; it is not an error. It means that the user did not finish the payment or the purchase is already finished.
The returned items already contain the amount and currency from the time of payment, because the Hive Axyl server keeps the order information. Do not overwrite them with the current price shown on the screen; save them as they are.
2. Prepare purchase information
- Recipe code Call the recipe code from the app.
Call PreparePurchaseAsync() with the retrieved item to prepare the purchase information to send to the app server. When you restore a PG purchase, this step does not call the Hive Axyl server; it only checks whether the retrieved item has a receipt.
PreparePurchaseOutcome prepared = await recipe.PreparePurchaseAsync(entry, token);
if (prepared.Pending != null)
{
// Replace the saved purchase information with the value the prepare step returned.
await pendingStore.SaveAsync(prepared.Pending);
}
if (prepared.Status != PreparePurchaseStatus.Success)
{
// Keep the saved purchase information and move on to the next item.
continue;
}
3. Verify receipts and deliver products
-
App code Implement this in the app.
App server Implement this on the app server.
Hive Axyl Server API Call the Hive Axyl Server API from the app server.
Detailed procedure: Verify receipts with the Hive Axyl Server API
For restored purchases, as for new purchases, the app server verifies the receipt and delivers the product. Send prepared.Pending from step 2 to the app server to request verification and delivery as a purchase restoration, and proceed to step 4 only when the app server responds with delivery completed.
3.1. Request verification and delivery from the app server
VerifyAndGrantOnceAsync() in the example code is the app's method implemented in Implement PG payments. For restoration, pass 2, which indicates a purchase restoration, as the second argument requestType. The usage example passes PurchaseVerificationKind.Recovery as this argument, and the app server integration code converts it to 2.
// Request receipt verification and product delivery from the app server as a purchase restoration.
PurchaseDeliveryConfirmation delivery = await VerifyAndGrantOnceAsync(
prepared.Pending,
requestType: 2, // 2: Purchase restoration. PurchaseVerificationKind.Recovery in the usage example
token);
if (delivery == null)
{
// The app server did not confirm delivery.
// Keep the saved purchase information and move on to the next item.
continue;
}
3.2. Verify receipts and deliver products on the app server
Have the app server verify the receipt and deliver the product in the same way as in Implement PG payments. The differences from a new purchase are as follows.
- Put
2, which indicates a purchase restoration, inrequestTypeof the receipt verification request. - Check the same delivery records as for new purchases to see whether the purchase has already been delivered.
requestTypeis not the basis for preventing duplicate delivery. - If the purchase has already been delivered, do not deliver it again; respond with delivery completed. The app finishes the transaction in step 4 only if you respond with delivery completed.
Order.IapPayload of the retrieved item contains the iapPayload passed when the purchase started. If you put this value in the verification request, it comes back as is in hiveAxylPayload of the response, so use this value to decide which delivery target or server to deliver to.
4. Finish the transaction
- Recipe code Call the recipe code from the app.
When the app server responds with delivery completed, call ClosePurchaseAsync() to finish the purchase. If you do not finish it, it is retrieved again in the next restoration.
The recipe performs the following tasks internally.
| Category | Call | Where to check |
|---|---|---|
| Hive Axyl SDK | Request purchase finalization from the server with FinalizePurchaseAsync() | Finalize the purchase |
5. Record delivery results
-
Hive Axyl SDK Call the Hive Axyl SDK from the app.
Detailed procedure: Save product delivery results
Record the delivery result on the Hive Axyl server. This is the same as when you process a new purchase.
In AxylTransactionId, put the hiveAxylTransactionId that VerifyAndGrantOnceAsync() in 3.1 kept separately from the app server response. This value is not contained in PurchaseDeliveryConfirmation.
Failure handling
Restoration iterates over multiple items, so even if one item fails, keep processing the rest. What the app does in each situation is as follows.
- Empty query result: A normal state with no purchases to finish
- Query failure: A state you must distinguish from an empty list. Record the error and query again at the next run
- Preparation failure: Keep the saved purchase information and proceed to the next item
- Delivery not confirmed by the app server: Keep the saved purchase information and proceed to the next item
- Finishing failure: Verification and delivery are already done. It is retrieved again in the next restoration, so you can leave it as is
Do not query repeatedly right after payment
Right after the user returns from the payment page, the payment may not be reflected yet. Do not have the app query repeatedly at short intervals; query when the user requests it again.
For how to handle each BusinessOutcome value, see Rejection reasons in payment recipes.
Next steps
For the procedure to implement new purchases, see Implement PG payments.