Skip to content

Purchase flow

levching edited this page Apr 15, 2020 · 3 revisions

Your application should add an observer to the payment queue during application initialization. If there are no observers attached to the queue, the payment queue does not synchronize its list of pending transactions with the Apple App Store, because there is no observer to respond to updated transactions. If an application quits when transactions are still being processed, those transactions are not lost. The next time the application launches, the payment queue will resume processing the transactions. Your application should always expect to be notified of completed transactions. If more than one transaction observer is attached to the payment queue, no guarantees are made as to the order they will be called in. It is safe for multiple observers to call FinishTransaction, but not recommended. It is recommended that you use a single observer to process and finish the transaction.

The code snippet below shows how to create simple transaction observer, attach it to a payment queue and respond to observer events:

Attaching the observer:

using SA.iOS.StoreKit;
...
MyTransactionObserver  Observer = new MyTransactionObserver();
ISN_SKPaymentQueue.AddTransactionObserver(Observer);

Observer implementation example. Please read comments inside the example.

using UnityEngine;
using SA.iOS.StoreKit;
using SA.Foundation.Templates;

public class MyTransactionObserver : ISN_iSKPaymentTransactionObserver
{

    //--------------------------------------
    //  ISN_TransactionObserver implementation
    //--------------------------------------

    public void OnTransactionUpdated(ISN_SKPaymentTransaction transaction) {

        //Transactions have been updated.
        //Let's act accordinaly
        Debug.Log("transaction JSON: " + JsonUtility.ToJson(transaction));

        Debug.Log("OnTransactionComplete: " + transaction.ProductIdentifier);
        Debug.Log("OnTransactionComplete: state: " + transaction.State);

        switch (transaction.State) {

            case ISN_SKPaymentTransactionState.Purchasing:
                //No actions is reuiredhere, we probably don't even have a ProductIdentifier
                //but we can use this callback to show preloader for example, 
                //since we know that user is currently
                //working on this transaction
                break;

            case ISN_SKPaymentTransactionState.Purchased:
            case ISN_SKPaymentTransactionState.Restored:
                //Our product has been succsesly purchased or restored
                //So we need to provide content to our user depends on productIdentifier
                UnlockProducts(transaction);

                break;
            case ISN_SKPaymentTransactionState.Deferred:
                //iOS 8 introduces Ask to Buy, which lets parents approve any 
                //purchases initiated by children
                //You should update your UI to reflect this deferred state, 
                //and expect another Transaction Complete to be called again 
                //with a new transaction state 
                //reflecting the parent’s decision or after the transaction times out. 
                //Avoid blocking your UI or gameplay while waiting 
                //for the transaction to be updated.
                break;
            case ISN_SKPaymentTransactionState.Failed:
                //Our purchase flow is failed.
                //We can unlock intrefase and repor user that the purchase is failed. 
                Debug.Log("Transaction failed, code: " + transaction.Error.Code);
                Debug.Log("Transaction failed, description: " + transaction.Error.Message);

                //at this point we just need to finish the transaction
                ISN_SKPaymentQueue.FinishTransaction(transaction);
                break;
        }

        if (transaction.State == ISN_SKPaymentTransactionState.Failed) {
            Debug.Log("Error code: " + transaction.Error.Code + "\n" + "Error description:" + transaction.Error.Message);
        } else {
            Debug.Log("product " + transaction.ProductIdentifier + " state: " + transaction.State.ToString());
        }
    }

    public void OnTransactionRemoved(ISN_SKPaymentTransaction result) {
        //Your application does not typically need to do anything on this event,  
        //but it may be used to update user interface to reflect that a transaction has been removed.
    }

    public bool OnShouldAddStorePayment(ISN_SKProduct result) {
        /// Return true to continue the transaction in your app.
        /// Return false to defer or cancel the transaction.
        /// If you return false, you can continue the transaction later using requetsed <see cref="ISN_SKProduct"/>
        /// 
        /// we are okay, to continue trsansaction, so let's return true
        return true;
    }

    public void OnRestoreTransactionsComplete(SA_Result result) {

        /// Tells the observer that the payment queue has finished sending restored transactions.
        /// 
        /// This method is called after all restorable transactions have been processed by the payment queue. 
        /// Your application is not required to do anything in this method.

        if (result.IsSucceeded) {
            Debug.Log("Restore Compleated");
        } else {
            Debug.Log("Error: " + result.Error.Code + " message: " + result.Error.Message);
        }
    }

    //--------------------------------------
    //  Private Methods
    //--------------------------------------

    private static void UnlockProducts(ISN_SKPaymentTransaction transaction)  {

        //At this point user has already paid for content, so we need to provide it
        //Unless, we want to make sure that payment was legit, and nobody is trying to hack us
        //In ordrer to do it, we have to use server side verification.
        //This step isn't required. Use it only if you want to make sure that payment is 100% legit
        //So far let's just print a Base64 receipt data

      //  Debug.Log("Receipt: " + ISN_SKPaymentQueue.AppStoreReceipt.AsBase64StringString);

        switch (transaction.ProductIdentifier) {
            case SMALL_PACK:
                //code for adding small game money amount here
                break;
            case NC_PACK:
                //code for unlocking cool item here
                break;

        }

        //After contect was provided to use we can finaly finish the transaction
        ISN_SKPaymentQueue.FinishTransaction(transaction);
    }

}

Add Payment Request

When a payment request is added to the queue, the payment queue processes that request with the Apple App Store and arranges for payment from the user. When that transaction is complete or if a failure occurs, the payment queue sends the ISN_SKPaymentTransaction object that encapsulates the request to all transaction observers.

Example how to add payment request:

using SA.iOS.StoreKit;
...
string SMALL_PACK = "your.product.id1.here";
ISN_SKPaymentQueue.AddPayment(ISN_PaymentManagerExample.SMALL_PACK);

Warning. Once your observer processes the transaction, you NEED to call FinishTransaction despite was it was successful or not. If you do not call the FinishTransaction method, when a user tries to purchase the same product, you will not get any callbacks during a currently active session. Only during the next application session when you will add your observer again, you will be notified about thew unfinished transaction.

Restore Completed Transactions

According to the Apple policy, if your application has Non-Consumable in-app items, you have to provide an ability to restore previously completed purchases for your user. For example, your application would use this to allow a user to unlock previously purchased content onto a new device.

Example how to start the restore flow:

using SA.iOS.StoreKit;
...
ISN_SKPaymentQueue.RestoreCompletedTransactions();

About

Foundation

AV Foundation

App Tracking Transparency

Game Kit

Store Kit

UI Kit

Social

Replay Kit

Contacts

AVKit

Photos

App Delegate

User Notifications

MediaPlayer

Core Location

AdSupport

EventKit

CloudKit

Authentication Services

XCode

Knowledge Base

Clone this wiki locally