Paid Apps

Implementing a paid app

A paid app is one that requires a business to make a purchase in order to access and use it. The subscription governs the app's lifecycle, ensuring that access is tied to ongoing payments. From the app's perspective, managing its lifecycle is crucial to ensure that the business continues to pay for its use

Subscription States

InTandem subscriptions are structured to exist in one of three states:

  • Purchased - The initial status upon subscription creation, signifying that the business has earned the rights to the subscription's assets, whether through direct payment or as part of a bundle with another subscription.
  • Canceled - A cancel request was sent. The package is still available until expriration_date date reaches.
  • Expired - This status indicates that the business's rights to the subscription's assets have ceased, either due to the cancellation of the subscription or the completion of a dunning process.

For Purchased, and Canceled states, the is_active property of a subscription returns true. When the subscription Expires, the is_active property turns to false.

Retrieving the business's subscription

At any stage of the app lifecycle the app can query the subscription to actively get the its state and is_active properties.

An example of the Get Subscriptions API call:

curl --location --globoff 'https://api.vcita.biz/business/subscriptionsmng/v1/subscriptions?[filter][business_uid]={business uid}' \
--header 'Authorization: Bearer {app-business token}' \
--header 'Content-Type: application/json' \

The above call response:

{
    "data": {
        "subscriptions": [
            {
                "uid": "b7ba1466-771e-432b-9c75-d738942bd7de",
                "display_name": "my paid app",
                "purchasable_uid": "ee655c26-4a57-48df-8e2e-f85a994b335c",
                "buyer_uid": "z7oeioxaqhinvcaa",
                "business_uid": "aasd2adfdsf34a",
                "purchase_currency": "USD",
                "purchase_price": "20.00",
                "purchase_state": "purchased",
                "cancellation_date": null,
                "expiration_date": null,
                "updated_at": "2024-03-24T12:54:16.000Z",
                "created_at": "2024-03-19T07:50:01.594Z",
                "payment_type": "monthly",
                "bundled_from_subscription_uid": null,
                "is_active": true
            }
        ]
    },
    "success": true
}

Get app-business token

To call the above API the app needs an app-business token. To obtain this token the app need to call the following API:

curl --location --request POST 'https://https://api.vcita.biz/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=assertion&type=token&business_uid=BUSINESS_UID'

Query parameters:

  • client_id - The app's client id provided when the app was created
  • client_secret - The app's secret provided when the app was created
  • grant_type - Use assertion to request an app-business access
  • type - Use token to request the access in the format of a token
  • business_uid - The business unique identifier

An example of the get app-business token response:

{
    "access_token": "69bf826a0dbe4c97822a29cb9c734707557fc6316481cfd0a20ffb6a47e39ef5",
    "token_type": "Bearer",
    "expires_in": 631139040,
    "created_at": 1725341277,
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI5Im1kNzBNMUZ5LUZnTVJmUV3yQUl2d3lwOVluaUdfNndtSmpQcWVhcDV2MGsifQ.eyJpc3MiOiJodHRwczovL3ZjaXRhLmNvbSIsInN1YiI6IndyYmZhcGgya2VpaTJuOGMiLCJhdWQiOiJmNjIyNDFmOTM3NjQ0ZjlmMjVjZjAwNzVlN2UwMDc5MTgyMDVjZDBkMGVkYTNjMWVmNTJmMDcxNzY0MDUwZDE4IiwiZXhwIjoxNzI1MzQxMzk3LCJpYXQiOjE3MjUzNDEyNzcsInR5cGUiOiJzdGFmZiIsImJ1c2luZXNzX2lkIjoibnZ2MWt3YjVycmlxcHVvYyIsImJ1c2luZXNzX3VpZCI6Im52djFrd2I1cnJpcXB1b2MiLCJidXNpbmVzc19uYW1lIjoiSWRvIG1haW4iLCJlbWFpbCI6Imlkby5kdmlyK21haW4xQHZjaXRhLmNvbSIsInJvbGUiOiJvd25lciIsImJyYW5kX2hvc3QiOiJ3d3cudmNpdGEuY29tIiwiYnJhbmRfdGhlbWUiOnsiY29sb3JzIjp7InByaW1hcnkiOiIjMTMxYTQ2Iiwic2Vjb25kYXJ5IjoiI2QxYjA1MCIsInRleHQiOiIjZmZmZmZmIn0sImxvZ28iOm51bGwsImxvY2FsZSI6ImVuIiwibmFtZSI6bnVsbCwicG93ZXJlZF9ieSI6eyJwYXJlbnRfbG9nb19icmlnaHQiOm51bGwsInBhcmVudF9sb2dvX2RhcmsiOm51bGwsInBhcmVudF9nb3RvX3VybCI6bnVsbCwiaW50YW5kZW1fbG9nb19icmlnaHQiOm51bGwsImludGFuZGVtX2xvZ29fZGFyayI6bnVsbCwiaW50YW5kZW1fZ290b191cmwiOm51bGx9fSwiZGlyZWN0b3J5X3VpZCI6ImlubG80cm0zbnRkODg2czYifQ.ZnUobei2JYHaReKdsvU6Fq_vtQn38CbB4BS2sBDRu9Ax5rdw6nDMjXxTq2HFOYr7IUIlfveENA4kXoRog7gWLFHAdAGJzqiiP9_1xRVDOi-nQSpyJLzi29IiMqCQyXOQhLXQMw70gXtEDr5oBriQZ8efiE9uzNKsnaaj5evMXy2WhuNj7VF61iL7944DnPUWfxl-tH109hM08oCsCxXqG2IDY3ar7WwYGcpcBxnzmda5JgMlfRVK1Q9r9nCRE9yprTIfllu0LmyI3CVU2Bg44JAPwajb3av5WvyP6tTJckazQDVPGN6QmywGbUrRI2bAHYQnyihrk-fGcY-7JUz0oQ"
}

Use the access_token in the above response as the app-business token in the get subscriptions API call.

Subscription webhooks

The app can register to the following webhooks:

The below flows utilize the subscription created/updated webhooks. Following are examples of their payloads:

Subscription Created webhook:

{
  "entity_name": "purchasable_subscription",
  "event_type": "created",
  "data": {
    "uid": "3d3dc97a-7ea7-42d7-a287-9993530721fd",
    "display_name": "myappcodename",
    "purchasable_uid": "ee655c26-4a57-48df-8e2e-f85a994b335c",
    "buyer_uid": "ny1g1gau6o3x3yat",
    "business_uid": "ato1v2g3eg3xkyqm",
    "purchase_currency": "USD",
    "purchase_price": "5.00",
    "purchase_state": "purchased",
    "cancellation_date": null,
    "expiration_date": null,
    "updated_at": "2024-03-01T12:27:09.966Z",
    "created_at": "2024-03-01T12:27:09.966Z",
    "payment_type": "monthly",
    "bundled_from_subscription_uid": null,
    "is_active": true
    }
}

Subscription Updated webhook:

{
  "entity_name": "purchasable_subscription",
  "event_type": "update",
  "data": {
    "uid": "3d3dc97a-7ea7-42d7-a287-9993530721fd",
    "display_name": "myappcodename",
    "purchasable_uid": "ee655c26-4a57-48df-8e2e-f85a994b335c",
    "buyer_uid": "ny1g1gau6o3x3yat",
    "business_uid": "ato1v2g3eg3xkyqm",
    "purchase_currency": "USD",
    "purchase_price": "5.00",
    "purchase_state": "canceled",
    "cancellation_date": "2024-04-17T12:27:10.000Z",
    "expiration_date": null,
    "updated_at": "2024-03-17T12:27:10.000Z",
    "created_at": "2024-03-01T12:27:09.966Z",
    "payment_type": "monthly",
    "bundled_from_subscription_uid": null,
    "is_active": true
    }
}

Subscription's live cycle

Purchase

Step-by-Step

  1. Purchase app - The user clicks on the Purchase app CTA in the app info page.
  2. Payment result - Following a successful payment a new subscription is created and the app is installed for the business to use.

Listening to the subscription created event, the app can get the newly created subscription and check its status (is_active=true), to allow the business to use it.

Cancel subscription

Step-by-Step

  1. Uninstalling - An admin clicks on the Uninstall CTA in the app info page. This action changes the subscription's state to canceled, and sets the cancellation date. A Subscription Update webhook is fired. The app can still be used (until the end of the billing cycle period).
    Listening to the Subscription Update webhook, the app gets the status change, but should still allow the user to fully use it (is_active=true)
  2. Expiry date reached - When the current billing cycle ends, the subscription expires. A Subscription Update and App Uninstall webhooks are fired.
    Listening to the Subscription Update webhook, the app gets the subscription's new state - expired, and is_active=false. At this point, the app should not allow the user to use it any more.

Dunning

Step-by-Step

  1. Payment failed - A subscription's renewal payment failed. The billing system will try to re-bill the business for a certain period of time. There is no change to the app's subscription, and the business can use it.
  2. Dunning process completed - The billing system didn't manage to collect the payment, and expires the subscription. The business is no longer eligible to use the app.
    Listening to the Subscription Update webhook, the app gets the subscription status is_active=false and should block any user