Skip to main content

In-App Purchases and Storefront

Many platforms (iOS, Android, Roku, etc.) require that all commerce transactions within an application utilize the platform's App Store. Zapp Supports several direct App Store integrations, enabling end-users to purchase Direct-To-Consumer Products via the App Store as a "Payment Gateway".

The Flow

  1. User navigates to the storefront screen in the app (via the app's settings screen or invoking it when tapping on a non free item).
  2. App presents Storefront screen with products pulled from the Store using the In-App Purchase (IAP) native library, matching the products retrieved from the payment provider, filtered by the required entitlements for a specific item (when applicable).
  3. User taps on the desired product.
  4. App will perform a purchase request using the IAP native library.
  5. Upon successful purchase, the app will notify the Payment provider with the receipt information (using the Notify Payment endpoint).
  6. The Payment provider will then send an acknowledgement response with the Location URL for the App to poll.
  7. Once the payment will be acknowledged, the app will notify the user with a success message, and proceed to the item or to the next screen.

Integration

Zapp provides an API to integrate with payment providers (which supports App store integrations).

In order to integrate with Zapp's Storefront Plugin it is required to implement the following endpoints:

Products endpoint

In order to present the Storefront screen and packages it is required to provide a feed which contains the mapping between store products and the payment provider products.

GET https://my-payment-provider.com/products

The server should respond with the following response, complying to Feed JSON Schema:

{
"type": {
"value": "<payment_provider>" // payment_provider is the name of the payment provider
},
"entry": [
{
"id": "<product_id>",
"type": {
"value": "<type>" // subscription, consumable
},
"extensions": {
"external_sku": {
"roku": "<ROKU_SKU>",
"amazon": "<AMAZON_SKU>",
"apple_store": "<APPLE_STORE_SKU>",
"google_play": "<GOOGLE_PLAY_SKU>"",
},
}
},
]
}

id - should represent the product id as set in the Payment provider dashboard.

external_sku - an object contains the SKUs as defined in the different stores console. These values are defined under the In App Purchases sections of the stores consoles.

Example of Store SKUs configurations

Apple (AppStore Connect):

Android (Google Play Developer Console):

Roku (Roku Developer Console):

Amazon (Amazon Developer Console):

On Amazon, make sure you configure the product under Subscription terms section.

This mapping is used to perform the purchase action. Without mapping the purchase will fail. Each platform requires its own store SKU, you can filter the external sku based on the platform context key.

The Storefront screen will use the data retrieved from the store for pricing, title and description - this is required in order to present the "localized" values, based on the account that the user set on the device.

Note In many cases, this endpoint will require sending additional Context keys such as access_token or id_token (as Bearer token or query param) after successful authentication.

Transactions endpoint

POST https://authorization-server.com/transactions
Request Payload (sent as JSON body):

{
"specversion": "1.0",
"type": "com.applicaster.payment.validate",
"source": "<APP_URLSCHEME>://<BUNDLE_ID>/versions/<VERSION>",
"subject": "Purchased product id <STORE_PRODUCT_ID> product name: <PRODUCT_NAME>",
"id": "<EVENT_UUID>",
"time": "<TIME_IN_RFC3339_FORMAT>",
"datacontenttype": "applicastion/json",
"data": {
"transaction": {
"storeProductId": "12345", // product id as defined in the store
"transactionId": "1234", // transaction id provided by the platforms
"userId": "abcd", // Optional - platform specific user id. In some cases this is mandatory - for example amazon
"receipt": "{}", // additional receipt data as provided by the platform
},
"itemId": "video_1", // Optional - The entry id initiated the purchase flow
"productId": "1245", //Product id as defined by the payment provider - the entry id provided in the response of the `products` endpoint.
"username": "test@gmail.com", //The username used to authenticate
"store": "apple_store/google_play/amazon/roku", //The store used to purchase the product,
"platform": "ios/tvos/android/amazon_fire_tv/roku"
}
}

This endpoint will be used to validate the receipt and acknowledge the purchase, after a successful purchase has been made. Usually the backend will accept the payment and validate the receipt using the Stores APIs (Google play, Apple store, Amazon, Roku).

As a response the apps will expect 1 of 2 responses:

  • For service that require asynchronous receipt validation of the store:
HTTP/1.1 202 Accepted
Location: https://example.com/access/1
Content-Type: application/vnd.api+json

{
"data": {
"type": "payment",
"id": "1"
}
}

The Location header URL will be used for polling (every 2 seconds, backend will determine the failure in case we want to control the maximum polling time) in order to get the response. Usually this endpoint will be the user entitlement check for a product.

  • For service that require synchronous receipt validation of the store:
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"type": "payment",
"id": "1"
}
}

This endpoint will require sending additional Context keys such as access_token or id_token (as Bearer token or query param) after successful authentication.

Entitlements endpoint

This endpoint will be use to check user entitlements, grant access to a specific product, or grant access to a specific set of products. During the process of a user trying to watch a video, the storefront screen should be prompted in case user does not have the relevant entitlements.

This endpoint could be used as the "polling endpoint" returned in the Location header described under the Transactions section.

GET example.com/entitlement/:productId

or

GET example.com/entitlements

The server should respond with: Success:

HTTP/1.1 200 OK
{
"type": "entitlement",
"id": "productId"
},

Failed:

HTTP/1.1 401 UNAUTHORIZED
{
"statusText": "Unauthorized",
"status": 401,
}

This endpoint necessitates sending additional Context Keys such as access_token or id_token (as Bearer token or query param) after successful authentication.

User Subscriptions endpoint (Active Purchases)

info

This endpoint is mandatory and must be implemented for Roku in-app billing to function correctly.

This endpoint retrieves all active purchases of the current user. For instance, it can be utilized to display this information within a "user account screen."

During the startup process of a Roku application, it is necessary to "silently" restore active subscriptions or purchases.

The endpoint is designed to return a Pipes2 Feed, which will be compared with the current user's purchases retrieved from the Roku Channel API. Entitlements will be verified against the payment provider using the Entitlements endpoint, as described earlier.

GET example.com/user-purchases

The server should respond with:

Success:

HTTP/1.1 200 OK
{
"id": "customer-1234-active-purchases",
"type": {
"value": "active-purchases"
},
"title": "Customer Purchases",
"entry": [
{
"type": {
"value": "subscription"
},
"id": "296bed86-11dc-43c3-9aaf-bfb25600ad2a", // This must be equal to the product id - it will be used to verify user entitlements
"title": "Monthly Subscription",
"extensions": {
"started_at": "March 16th, 2023",
"expires_at": "March 16th, 2024",
}
}
]
}

Failed:

HTTP/1.1 401 UNAUTHORIZED
{
"error": "Unauthorized",
"status": 401,
}

This endpoint necessitates sending additional Context Keys such as access_token or id_token (as Bearer token or query param) after successful authentication from previous sessions.