The Catappult Developer Hub

Welcome to the Catappult developer hub. You'll find comprehensive guides and documentation to help you start working with Catappult as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    

One Step Payment

Overview

One Step Payment(OSP) is the simpler way to start a AppCoins purchase. It’s done by creating a simple URL with payment information, to check by yourself how simple it is, visit this page.

One Step Payment flow

  1. Create an URL to start the purchase flow. The URL should follow the documentation found here. Feel free to check our online tool to check how simple it is.
  2. The developer has to make a simple system call with the URL created at step 1.
  3. AppCoins wallet will communicate with our services to process the payment
  4. Once the purchase is over, our server will notify the app’s services with all the details about it.
  5. App’s server should deliver the purchased item.

🚧

Note: Make sure you set the Callback URL field so our servers can notify yours once the purchase is completed.

Create URL (1)

Create an URL with the following structure:
Scheme: https
Host: apichain.catappult.io
Path: /transaction/inapp
Query string arguments: value, currency, to, product, domain, data, callback_url, reference, signature
Example:

https://apichain.catappult.io/transaction/inapp?value=11&currency=usd&domain=com.appcoins.trivialdrivesample&product=sword.001

🚧

Warning!

Previously there was a "to" argument where the destination wallet address would be specified. This argument is no longer needed and was removed from this example.

Query String Arguments details

Overview

ParameterTypeDescription_of_fieldStaticOptionalExample
value*DoubleThe value in APPC (AppCoins by default) of the transaction.NN11.5
currencyStringThe currency in which the value is sent, if no currency is sent it is considered APPC (AppCoins).NYUSD or EUR
product*StringThe id of the item being bought.YNsword.001
domain*StringThe application id, also known as package name.YNcom.appcoins.trivialdrivesample
dataStringAdditional information to be sent if needed.NYAwesome Sword
callback_urlStringThe developer's URL to be called after the transaction is completed.NYhttps://mygamestudio.co/appcoins?out_trade_no=1234
order_referenceStringUnique identifier of the transaction created by the developer. (Cannot be used for different purchases)NYXYZ98880032
signatureStringThe Hexadecimal string of the signed URL in order to be validated. For more details see section Content Validation.NY49bc6dac9780acfe5419eb16e862cf096994c15f807313b04f5a6ccd7717e78e

* mandatory fields

URL call (2)

Inside the app / game, an intent with the URL should be opened either with the browser, useful when the AppCoins Wallet is not installed, or by the AppCoins Wallet. In some cases the user chooses to open the URL with the browser when the AppCoins is already installed or even chooses the option to always open the URL with the browser.
So in order to avoid this problem we suggested the use of the below sample to create the intent to trigger the One Step billing flow.

Intent intent = buildTargetIntent(url);
try {
    startActivityForResult(intent, RC_ONE_STEP);
} catch (Exception e) {
    e.printStackTrace();
}
/**
* This method generates the intent with the provided One Step URL to target the
* AppCoins Wallet.
* @param url The url that generated by following the One Step payment rules
* 
* @return The intent used to call the wallet 
*/
private Intent buildTargetIntent(String url) {
  Intent intent = new Intent(Intent.ACTION_VIEW);
  intent.setData(Uri.parse(url));

  // Check if there is an application that can process the AppCoins Billing
  // flow
  PackageManager packageManager = getApplicationContext().getPackageManager();
  List<ResolveInfo> appsList = packageManager
            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
  for (ResolveInfo app : appsList) {
    if (app.activityInfo.packageName.equals("cm.aptoide.pt")) {
      // If there's aptoide installed always choose Aptoide as default to open 
      // url
      intent.setPackage(app.activityInfo.packageName);
      break;
    } else if (app.activityInfo.packageName.equals("com.appcoins.wallet")) {
      // If Aptoide is not installed and wallet is installed then choose Wallet
      // as default to open url
      intent.setPackage(app.activityInfo.packageName);
    }
  }
  return intent;
}

Payment Notification (4)

How can the developer be sure that the purchase is valid ?
If a callback url is provided by the developer, our purchase service triggers a notification when a purchase is completed. In order to use this service you must:

📘

You can check https://github.com/Aptoide/appcoins-server-validator for a fully working sample

Set the callback_url

The composition of the callback URL can contain as query string any parameter relevant to the transaction so that it can be handled on the developer's side. As an example, the following url beside the endpoint contains also a transaction id relevant to the developer. Checkout the Payment Notification section to know more.

https://www.mygamestudio.com/v1/appcoins_ipn.php?out_trade_no=2082389608326064

Prepare your server

Our service will call the provided callback_url using the following specification:

Overview

Method: Post
URL: The provided URL
Body: signature, transaction
Content type: Json
Response code: 200

❗️

Response code

It's important that your service return 200 as status code so we acknowledge you did receive the notification successfully.

Body example

{
  "signature": "62747e2bd871b38794edf8e7e27fc525f523be4955beeb2467ba8617d10ef1ff3c39eac5c75a52c8ebc0badefec32a84616f26ebc0213fa86604567c218b74931b",
  "transaction": "{\"uid\":\"2DtyvTOSShc1xT9C\",\"domain\":\"com.appcoins.trivialdrivesample\",\"product\":\"gas\",\"reference\":\"XYZ98880032\",\"status\":\"COMPLETED\",\"added\":\"2020-05-04T10:19:44+00:00\",\"modified\":\"2020-05-04T10:19:44+00:00\",\"price\":{\"currency\":\"APPC\",\"value\":\"1\",\"appc\":\"1\"}}"
}

Body structure

Details about parameters present at the root of the json object sent when calling callback_url:

TypeNameDescriptionSchemaExample
Bodysignature(Deprecated)
Notification signature based on transaction parameter
string0x7b87a3c4dd63bee43d4c88
BodytransactionTransaction resource in jsonstring

Signature

Deprecated: This argument is no longer used

Transaction parameter fields

This parameter is a json structured string should be parsed to get the information about the purchase itself

NameDescriptionSchemaExample
uidUnique ID for transaction resourcestring2DtyvTOSShc1xT9C
domainPackage namestringcom.appcoins.trivialdrivesample
productProduct name (aka SKU)stringsword.001
referenceUnique identifier of the transaction created by the developer.stringXYZ98880032
statusTransaction statusstringCOMPLETED or CANCELED or FAILED
addedTransaction added timestmapstring2020-04-18T06:15:18+00:00
modifiedTransaction modified timestmapstring2020-04-18T07:17:19+00:00
price.appcTransaction price in AppCoinsstring115
price.currencyTransaction price currency (used by user to perform the purchase)stringUSD, APPC, EUR, etc
price.valueTransaction price valuestring11.5

Verify data integrity

You can verify if that all the data sent in transaction parameter is indeed generated by Catappult platform using our transactions api:
Method: GET
Scheme: https
HOST: api.blockchainds.com
Path: /broker/8.20200101/transactions/{uid}
Ex:

https://api.blockchainds.com/broker/8.20200101/transactions/2DtyvTOSShc1xT9C

Response:

{
  "uid": "2DtyvTOSShc1xT9C",
  "domain": "com.appcoins.trivialdrivesample",
  "product": "gas",
  "wallet_from": "0xa43748bf498d7070d05d4fde042c51c780ce71b9",
  "country": "PT",
  "type": "INAPP_UNMANAGED",
  "reference": "XYZ98880032",
  "hash": "0x234e3c2407680ffe07d4f1bb7bc5c773085cd4ca723669a0473777ceeaabab95",
  "origin": "BDS",
  "status": "COMPLETED",
  "added": "2020-05-04T10:19:45+00:00",
  "modified": "2020-05-04T10:19:45+00:00",
  "gateway": {
    "name": "appcoins_credits"
  },
  "metadata": null,
  "price": {
    "currency": "APPC",
    "value": "1",
    "appc": "1"
  }
}

The data that you get from the transaction argument should be the equals to the data you get from transactions api call.
Here's an example of how you can do it:

<?php
require_once "vendor/autoload.php";
use GuzzleHttp\Client;
function is_valid($notification_transaction, $webservice_transaction) {
    return $notification_transaction->uid === $webservice_transaction->uid &&
        $notification_transaction->domain === $webservice_transaction->domain &&
        $notification_transaction->product === $webservice_transaction->product &&
        $notification_transaction->status === $webservice_transaction->status &&
        $notification_transaction->reference === $webservice_transaction->reference &&
        $notification_transaction->price->currency === $webservice_transaction->price->currency &&
        $notification_transaction->price->value === $webservice_transaction->price->value &&
        $notification_transaction->price->appc === $webservice_transaction->price->appc;
}
#notification you receive on your backend in callback_url service
$notification = '
  "signature": "62747e2bd871b38794edf8e7e27fc525f523be4955beeb2467ba8617d10ef1ff3c39eac5c75a52c8ebc0badefec32a84616f26ebc0213fa86604567c218b74931b",
  "transaction": "\\"uid\\":\\"2DtyvTOSShc1xT9C\\",\\"domain\\":\\"com.appcoins.trivialdrivesample\\",\\"product\\":\\"gas\\",\\"status\\":\\"COMPLETED\\",\\"added\\":\\"2020-05-04T10:19:44+00:00\\",\\"modified\\":\\"2020-05-04T10:19:44+00:00\\",\\"price\\":\\"currency\\":\\"APPC\\",\\"value\\":\\"1\\",\\"appc\\":\\"1\\""
';
$notification_transaction = json_decode(json_decode($notification)->transaction);
$client = new Client();
$transaction_response = json_decode($client->get(
    'https://api.blockchainds.com/broker/8.20200101/transactions/'.$notification_transaction->uid
)->getBody());
var_dump(is_valid($notification_transaction, $transaction_response));
import json
import requests


def is_valid(notification_transaction, webservice_transaction):
    return notification_transaction['uid'] == webservice_transaction['uid'] and \
           notification_transaction['domain'] == webservice_transaction['domain'] and \
           notification_transaction['product'] == webservice_transaction['product'] and \
           notification_transaction['status'] == webservice_transaction['status'] and \
           notification_transaction['reference'] == webservice_transaction['reference'] and \
           notification_transaction['price'] == webservice_transaction['price'] and \
           notification_transaction['price']['currency'] == webservice_transaction['price']['currency'] and \
           notification_transaction['price']['value'] == webservice_transaction['price']['value'] and \
           notification_transaction['price']['appc'] == webservice_transaction['price']['appc']


notification = """{
  "signature": "62747e2bd871b38794edf8e7e27fc525f523be4955beeb2467ba8617d10ef1ff3c39eac5c75a52c8ebc0badefec32a84616f26ebc0213fa86604567c218b74931b",
  "transaction": "{\\"uid\\":\\"2DtyvTOSShc1xT9C\\",\\"domain\\":\\"com.appcoins.trivialdrivesample\\",\\"product\\":\\"gas\\",\\"reference\\":\\"XYZ98880032\\",\\"status\\":\\"COMPLETED\\",\\"added\\":\\"2020-05-04T10:19:44+00:00\\",\\"modified\\":\\"2020-05-04T10:19:44+00:00\\",\\"price\\":{\\"currency\\":\\"APPC\\",\\"value\\":\\"1\\",\\"appc\\":\\"1\\"}}"
}"""

notification_transaction = json.loads(json.loads(notification)['transaction'])
transaction_response = requests.get(
    'https://api.blockchainds.com/broker/8.20200101/transactions/{}'.format(
        notification_transaction['uid'])).json()

print(is_valid(notification_transaction, transaction_response))

URL Content Validation (optional)

For extra security of the URL used to start the purchase, in order to validate if the URL content has not been tampered, the developer can include the signature parameter in the URL. The signature parameter is built as shown below, where the URL is signed using a HMAC function with the use of SHA256 algorithm.
The required secret key should be only available at a server level and should be shared between the developer and provider. This field is not mandatory for the URL to work.

<?php
 
function generate_url(): string
{
    $secret = 'foobar';
   
    $url = 'https://apichain.catappult.io/transaction/inapp?value=2.50&currency=APPC&to=0xab949343e6c369c6b17c7ae302c1debd4b7b61c3&product=gems.001&domain=com.mygamestudio.game&data=100+gems&callback_url=https%3A%2F%2Fwww.mygamestudio.com%2Fappcoins%3Fout_trade_no%3D1234&reference=XYZ98880032';
   
    $signature = hash_hmac('sha256', $url, $secret, false);
   
    return $url . '&signature=' . $signature;
}
 
echo generate_url();
 
?>

🚧

Avoid having the secret key in the APK and instead sign it server side. Having the secret key in the APK may compromise the security delivered with this step of the URL building, since it can be easily obtained by anyone that has access to APK.

Updated 11 days ago

One Step Payment


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.