The iOS Billing SDK is a simple solution to implement Catappult billing. It consists of a Billing client that uses an AppCoins Wallet integration and allows you to get your products from Catappult and process the purchase of those items.
The billing flow in your application with the SDK is as follows:
- Setup the AppCoins SDK Swift Package;
- Query your In-App Products;
- User wants to purchase a product;
- Application starts the purchase and the SDK handles it, returning the purchase status and validation data on completion;
- Application gives the product to the user.
-
Add AppCoins SDK Swift Package
In XCode add the Swift Package from the repo https://github.com/Catappult/appcoins-sdk-ios.git. -
Add AppCoins SDK Keychain Access Entitlement
In order to enable the AppCoins SDK to save the user’s AppCoins Wallet information in the keychain, the application will need to concede the SDK Keychain Access entitlements. To do so, follow these steps:- Select your project in the project navigator (left sidebar);
- Select your target under "TARGETS";
- Go to the "Signing & Capabilities" tab;
- Click the "+" button to add a new capability;
- Search for "Keychain Sharing" and select it;
- Enable the "Keychain Sharing" capability by double-clicking it;
- This will automatically write your app’s identifier in the "Keychain Groups" text box, you should replace it with “com.aptoide.appcoins-wallet“;
- Xcode will automatically generate an entitlements file (e.g., YourAppName.entitlements) and add it to your project;
-
Add AppCoins SDK URL Type
To manage redirect deep links for specific payment method integrations, your application must include a URL Type in the info.plist file. To do this, follow these steps:- In the project navigator (left sidebar), select your project.
- Under "TARGETS", select your target.
- Navigate to the "Info" tab.
- Scroll down to the "URL Types" section.
- Click the "+" button to add a new URL Type.
- Set the URL Scheme to "$(PRODUCT_BUNDLE_IDENTIFIER).iap" and the role to "Editor".
-
Add AppCoins Wallet Queried URL Scheme
To enable the SDK to detect the presence of the AppCoins Wallet app, allowing users to make payments using their Wallet, you need to include the AppCoins Wallet URL scheme in your list of queried URL schemes. Follow these steps to achieve this:- In the project navigator (left sidebar), select your project.
- Under "TARGETS", select your target.
- Navigate to the "Info" tab.
- If it doesn't already exist, add a new property called "Queried URL Schemes".
- To add the AppCoins Wallet URL scheme, click the "+" button.
- Set the URL Scheme field to "com.aptoide.appcoins-wallet".
Now that you have the SDK and necessary permissions set-up you can start making use of its functionalities. To do so you must import the SDK module in any files you want to use it by calling the following: import AppCoinsSDK
.
-
Handle the Redirect
In yourSceneDelegate.swift
file (or in another entry point your application might have), you need to handle potential payment redirects when the application is brought to the foreground.func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { if AppcSDK.handle(redirectURL: URLContexts.first?.url) { return } // the rest of your code }
🚧 It is important that the
AppcSDK.handle
method precedes the rest of your code. -
Check AppCoins SDK Availability
The AppCoins SDK will only be available on devices in the European Union with an iOS version equal to or higher than 17.4. Therefore, before attempting any purchase, you should check if the SDK is available by callingAppcSDK.isAvailable
.if await AppcSDK.isAvailable() { // make purchase }
-
Query In-App Products
You should start by getting the In-App Products you want to make available to the user. You can do this by callingProduct.products
.This method can either return all of your Cattapult In-App Products or a specific list.
-
Product.products()
Returns all application Cattapult In-App Products:
let products = try await Product.products()
-
Product.products(for: [String])
Returns a specific list of Cattapult In-App Products:
let products = try await Product.products(for: ["gas"])
⚠️ Warning: You will only be able to query your In-App Products once your application is reviewed and approved on Catappult. -
-
Purchase In-App Product
To purchase an In-App Product you must call the functionpurchase()
on a Product object. The SDK will handle all of the purchase logic for you and it will return you on completion the result of the purchase. This result can be either.success(let verificationResult)
,.pending
,.userCancelled
or.failed(let error)
.In case of success the application will verify the transaction’s signature locally. After this verification you should handle its result:
– If the purchase is verified you should consume the item and give it to the user:
– If it is not verified you need to make a decision based on your business logic, you either still consume the item and give it to the user, or otherwise the purchase will not be acknowledged and we will refund the user in 24 hours.In case of failure you can deal with different types of error in a switch statement. Every error returned by the SDK is of type
AppCoinsSDKError
and it is described later in this document.You can also pass a Payload to the purchase method in order to associate some sort of information with a specific purchase. You can use this for example to associate a specific user with a Purchase:
gas.purchase(payload: "User123")
.
let result = await products?.first?.purchase() switch result { case .success(let verificationResult): switch verificationResult { case .verified(let purchase): // consume the item and give it to the user try await purchase.finish() case .unverified(let purchase, let verificationError): // deal with unverified transactions } case .pending: // transaction is not finished case .userCancelled: // user cancelled the transaction case .failed(let error): // deal with any possible errors }
-
Query Purchases
You can query the user’s purchases by using one of the following methods:-
Purchase.all
This method returns all purchases that the user has performed in your application.
let purchases = try await Purchase.all()
-
Purchase.latest(sku: String)
This method returns the latest user purchase for a specific In-App Product.
let purchase = try await Purchase.latest(sku: "gas")
-
Purchase.unfinished
This method returns all of the user’s unfinished purchases in the application. An unfinished purchase is any purchase that has neither been acknowledged (verified by the SDK) nor consumed. You can use this method for consuming any unfinished purchases.
let purchases = try await Purchase.unfinished()
-
To test the SDK integration during development, you'll need to set the installation source for development builds, simulating that the app is being distributed through Aptoide. This action will enable the SDK's isAvailable
method.
Follow these steps:
-
In your target build settings, search for "Marketplaces".
-
Under "Deployment", set the key "Marketplaces" or "Alternative Distribution - Marketplaces" to "com.aptoide.ios.store".
-
In your scheme, go to the "Run" tab, then navigate to the "Options" tab. In the "Distribution" dropdown, select "com.aptoide.ios.store".
For more information, please refer to Apple's official documentation: https://developer.apple.com/documentation/appdistribution/distributing-your-app-on-an-alternative-marketplace#Test-your-app-during-development
You can test the in-app purchase (IAP) functionality using Catappult’s Sandbox environment. Follow these steps to set it up:
- Retrieve your testing wallet address by calling
Sandbox.getTestingWalletAddress()
. - Add your testing wallet address to the Sandbox menu in the Developer Console, and use this wallet to make test purchases in your app.
⚠️ Warning: You must have proven ownership of the app to access the Sandbox environment and test IAPs.
⚠️ Warning: Do not delete the app from the testing device, as this will remove the testing wallet. If the app is deleted, you’ll need to obtain a new wallet address and add it again to the Sandbox.
- Select the item you wish to purchase in the app.
- Choose the Sandbox option for the transaction.
- Once the purchase is completed, verify the transaction in the Wallet by checking the Sandbox transactions.
- Ensure the purchased item is correctly received in the app.
If all steps are successful, your billing solution is fully integrated!
For more detailed instructions, refer to Catappult's documentation.
In order to add translations for different localizations, the application will need to add a Mixed Localizations permission. Follow these steps:
- In the Project Navigator (left sidebar), locate the "Info.plist" file. It is typically in the root folder of your project.
- Double-click on "Info.plist" to open it in the property list editor.
- Add a new row - Right-click on any existing key-value pair in the property list editor and choose "Add Row," or use the "+" button at the top of the editor.
- Set the key to
CFBundleAllowMixedLocalizations
. - Set the type of the new key to Boolean.
- Set the value to
YES
to allow mixed localizations.
The SDK integration is based on four main classes of objects that handle its logic:
Product
represents an in-app product. You can use it to either statically query products or use a specific instance to perform a purchase.
Properties:
sku
: String - Unique product identifier. Example: gastitle
: String - The product display title. Example: Best Gasdescription
: String? - The product description. Example: Buy gas to fill the tank.priceCurrency
: String - The user’s geolocalized currency. Example: EURpriceValue
: String - The value of the product in the specified currency. Example: 0.93priceLabel
: String - The label of the price displayed to the user. Example: €0.93priceSymbol
: String - The symbol of the geolocalized currency. Example: €
Purchase
represents an in-app purchase. You can use it to statically query the user’s purchases or use a specific instance to consume the respective purchase.
Properties:
uid
: String - Unique purchase identifier. Example: catappult.inapp.purchase.ABCDEFGHIJ1234sku
: String - Unique identifier for the product that was purchased. Example: gasstate
: String - The purchase state can be one of three: PENDING, ACKNOWLEDGED, and CONSUMED. Pending purchases are purchases that have neither been verified by the SDK nor have been consumed by the application. Acknowledged purchases are purchases that have been verified by the SDK but have not been consumed yet. Example: CONSUMEDorderUid
: String - The orderUid associated with the purchase. Example: ZWYXGYZCPWHZDZUK4Hpayload
: String - The developer Payload. Example: 707048467.998992created
: String - The creation date for the purchase. Example: 2023-01-01T10:21:29.014456Zverification
: PurchaseVerification - The verification data associated with the purchase.
PurchaseVerification
represents an in-app purchase verification data.
Properties:
type
: String - The type of verification made. Example: GOOGLEsignature
: String - The purchase signature. Example: C4x6cr0HJk0KkRqJXUrRAhdANespHEsyx6ajRjbG5G/v3uBzlthkUe8BO7NXH/1Yi/UhS5sk7huA+hB8EbaQK9bwaiV/Z3dISl5jgYqzSEz1c/PFPwVEHZTMrdU07i/q4FD33x0LZIxrv2XYbAcyNVRY3GLJpgzAB8NvKtumbWrbV6XG4gBmYl9w4oUgJLnedii02beKlvmR7suQcqIqlSKA9WEH2s7sCxB5+kYwjQ5oHttmOQENnJXlFRBQrhW89bl18rccF05ur71wNOU6KgMcwppUccvIfXUpDFKhXQs4Ut6c492/GX1+KzbhotDmxSLQb6aw6/l/kzaSxNyjHg==data
: PurchaseVerificationData - The data associated with the verification of the purchase.
PurchaseVerificationData
represents the body of an in-app purchase verification data.
Properties:
orderId
: String - The orderUid associated with the purchase. Example: 372EXWQFTVMKS6HIpackageName
: String - Bundle ID of the product's application. Example: com.appcoins.trivialdrivesampleproductId
: String - Unique identifier for the product that was purchased. Example: gaspurchaseTime
: Integer - The time the product was purchased. Example: 1583058465823purchaseToken
: String - The token provided to the user's device when the product was purchased. Example: catappult.inapp.purchase.SZYJ5ZRWUATW5YU2purchaseState
: Integer - The purchase state of the order. Possible values are: 0 (Purchased) and 1 (Canceled)developerPayload
: String - A developer-specified string that contains supplemental information about an order. Example: myOrderId:12345678
This class is responsible for general purpose methods such as handling redirects or checking if the SDK is available.
The error enum that can be returned by the SDK while performing any action.
Possible errors:
networkError
: Network related errors;systemError
: Internal APPC system errors;notEntitled
: The host app does not have proper entitlements configured;productUnavailable
: The product is not available;purchaseNotAllowed
: The user was not allowed to perform the purchase;unknown
: Other errors.