Billing Service

Like authentication, supporting multiple billing platforms can be a tremendous burden for game server developers. iFun Engine is designed to support multiple billing platforms with the same interface.

For implementing In-App Purchases using Google Play or Apple AppStore, follow the next steps. The game client uses the corresponding platform’s SDK for payment, and when the payment is complete, the item purchase information such as item information and receipt is sent to the game server. The game server validates the received receipt, for example whether it is a receipt that has already been used or not, and if valid, records the receipt information and issues the game item. The iFun Engine billing service provides the essential features for validating receipts and preventing the use of duplicate receipts.

iFun Engine-Biller Agent

Like the funapi-authenticator agent for authentication, billing uses the funapi-biller agent. The following external billing platforms are supported. (We plan to continuously add more supported platforms.)

  • Google Play

  • Apple AppStore

  • SK TStore

Installation

When developing a game server with iFun Engine, you can set use_biller to false as explained here to work in test mode. You can install later if you are evaluating or in the early stages of development and don’t need real billing.

Install the funapi auto-configuration package as explained here and install with the following commands.

$ sudo apt-get update
$ sudo apt-get install funapi-biller1

Modify to enabled=1 in the file opened with the following command.

$ sudo vim /etc/default/funapi-biller

Run Service

Start the service with the following command. (The log is created in /var/log/funapi/funapi-biller1/) Before starting the service, see below to configure the database information. If the database configuration is incorrect, the service will not start.

$ sudo service funapi-biller1 start

Configuration

Open the configuration file as below and modify the appropriate values.

sudo vim /usr/share/funapi-biller/manifests/src/MANIFEST.json

Usage

You can either use the provided iFun Engine API (if developing the game server with iFun Engine) or the independent HTTP Interface (RESTful API). Refer to the following sections.

MANIFEST.json configuration

To use the Billing feature, you need to configure the iFun Engine game to use the funapi-biller agent. See the MANIFEST.json section and the description below to configure billing.

  • Component name: BillingClient

  • Arguments

    • use_biller: Set as true or false. If false, bypass the receipt validation process and deem all receipt validation requests as successful. This is useful for the developer to bypass validation and test the game in a local environment.

    • remote_biller_ip_address: Set the IP address of funapi-biller. The default value is used if not set.

    • remote_biller_port: Set the port of funapi-biller. The default value is used if not set.

    • googleplay_refresh_token: This is the refresh token to access Google Play.

    • googleplay_client_id: This is required information to access Google Play.

    • googleplay_client_secret: This is required information to access Google Play.

Note

See the Google Play Authorization and enter the correct value.

Warning

Google Play uses the refresh token to issue an access token.

When an access token is issued, the previous access tokens are no longer valid. Thus, make sure the refresh token is not used anywhere else.

Example: Google Play

In this section, we’ll see the synchronous and asynchronous methods for Google Play receipt validation.

Synchronous methods

The following is example code for the case of a Facebook user making a payment in the middle of a game, validating the corresponding payment receipt, and recording the payment information for the Facebook user. The iFun Engine biller not only simply validates receipts, but also manages the user information for payments.

#include <funapi.h>

using namespace fun;

// Below, we assume that a Facebook user made the payment.
// The funapi-biller saves the user and payment information linked together.
void example(const string &facebook_id,
             const string &google_package_name, const string &google_product_id,
             const string &google_purchase_token) {

  // Call the iFun Engine utility function for Google Play to create a iFun Engine
  // receipt data structure.
  Receipt receipt = MakeGooglePlayReceipt(goole_package_name, google_product_id,
      google_purchase_token);

  // Create a receipt validation request.
  // The first argument is the authentication platform type.
  // The authentication platform is used in payment validation for linking
  // the payment information and user information together.
  // You can give a dummy value if you don't want any linking.
  ReceiptValidationRequest request("Facebook", facebook_id, receipt);
  ReceiptValidationResponse response;

  // Request receipt validation using the synchronous method.
  // This blocks until the result is ready.
  if (not ValidateReceiptSync(request, &response)) {
    // system error
    LOG(ERROR) << "billing system error";
    return;
  }

  // Issue the game item if validation is successful.
  if (response == kSuccess) {
    // Give the item matched to product_id
    // ...
    return;
  }

  // Validation was unsuccessful. Handle each failure case.
  if (response == kFailWrongReceipt) {
    // It was a wrong receipt. This may be the case where the client sent a
    // fake receipt.
    LOG(WARNING) << "wrong receipt";
  } else if (response == kFailAlreadyProvisioned) {
    // It was a receipt that has already been used.
    LOG(WARNING) << "already provisioned";
  } else {
    // Other errors.
    LOG(WARNING) << "receipt validation error: " << response;
  }
}

Asynchronous method

The asynchronous method is similar to the synchronous method, except that you call ValidateReceipt() instead of ValidateReceiptSync().

#include <funapi.h>

using namespace fun;


// Define the callback function called after validation.
// This callback function is called by iFun Engine automatically.
void OnReceiptValidated(
    const ReceiptValidationRequest &request,
    const ReceiptValidationResponse &response,
    const bool &error) {
  if (error) {
    // system error
    LOG(ERROR) << "billing system error";
    return;
  }

  if (response == kSuccess) {
    // Give the item matched to product_id
    // ...
    return;
  }

  if (response == kFailWrongReceipt) {
    LOG(WARNING) << "wrong receipt";
  } else if (response == kFailAlreadyProvisioned) {
    LOG(WARNING) << "already provisioned";
  } else {
    LOG(WARNING) << "receipt validation error: " << response;
  }
}


// Below, we assume that a Facebook user made the payment.
// The funapi-biller saves the user and payment information linked together.
void example(const string &facebook_id,
             const string &google_package_name, const string &google_product_id,
             const string &google_purchase_token) {
  // Call the iFun Engine utility function for Google Play to create a iFun Engine
  // receipt data structure.
  Receipt receipt = MakeGooglePlayReceipt(google_package_name, google_product_id,
      google_purchase_token);

  // Create a receipt validation request.
  // The first argument is the authentication platform type.
  // The authentication platform is used in payment validation for linking
  // the payment information and user information together.
  // You can give a dummy value if you don't want any linking.
  ReceiptValidationRequest request("Facebook", facebook_id, receipt);

  // Set the Callback function.
  BillingResponseHandler callback = OnReceiptValidated;

  // Start asynchronous validation.
  ValidateReceipt(request, callback);
}

Example: Apple AppStore

iFun Engine supports Apple AppStore payment receipt validation with the same interface. The only difference from the Google Play example is that a different utility function MakeAppleAppStoreReceipt() is used instead of MakeGooglePlayReceipt().

Synchronous method

#include <funapi.h>

using namespace fun;

// We assume that a Facebook user made the payment, same as the Google Play
// example.
// The funapi-biller saves the user and payment information linked together.
void example(const string &facebook_id,
             const string &apple_receipt_data, const string &apple_product_id,
             int quantity) {
  // Call the iFun Engine utility function for Apple to create a iFun Engine receipt
  // data structure.
  // This is the only part different from the Google play example.
  Receipt receipt = MakeAppleAppStoreReceipt(
      apple_receipt_data, apple_product_id, quantity);

  ReceiptValidationRequest request("Facebook", facebook_id, receipt);
  ReceiptValidationResponse response;

  if (not ValidateReceipt(request, &response)) {
    // system error
    LOG(ERROR) << "billing system error";
    return;
  }

  if (response == kSuccess) {
    // Give the item matched to product_id
    // ...
    return;
  }

  if (response == kFailWrongReceipt) {
    LOG(WARNING) << "wrong receipt";
  } else if (response == kFailAlreadyProvisioned) {
    LOG(WARNING) << "already provisioned";
  } else {
    LOG(WARNING) << "receipt validation error: " << response;
  }
}

Asynchronous method

#include <funapi.h>

using namespace fun;


void OnReceiptValidated(
    const ReceiptValidationRequest &request,
    const ReceiptValidationResponse &response,
    const bool &error) {
  if (error) {
    // system error
    LOG(ERROR) << "billing system error";
    return;
  }

  if (response == kSuccess) {
    // Give the item matched to product_id
    // ...
    return;
  }

  if (response == kFailWrongReceipt) {
    LOG(WARNING) << "wrong receipt";
  } else if (response == kFailAlreadyProvisioned) {
    LOG(WARNING) << "already provisioned";
  } else {
    LOG(WARNING) << "receipt validation error: " << response;
  }
}


// Below, we assume that a Facebook user made the payment.
// The funapi-biller saves the user and payment information linked together.
void example(const string &facebook_id,
             const string &apple_receipt_data, const string &apple_product_id,
             int quantity) {
  // Call the iFun Engine utility function for Apple to create a iFun Engine receipt
  // data structure.
  // This is the only part different from the Google play example.
  Receipt receipt = MakeAppleAppStoreReceipt(
      apple_receipt_data, apple_product_id, quantity);

  ReceiptValidationRequest request("Facebook", facebook_id, receipt);

  BillingResponseHandler callback = bind(&OnReceiptValidated, _1, _2, _3);
  ValidateReceipt(request, callback);
}

iFun Engine-Biller HTTP Interface

We provide an API for using the funapi-biller agent independent of the iFun Engine API. As specified below, you can set each parameter in JSON format in the body of an HTTP post request and send it to the funapi-biller.

Authentication

POST /v1/authentication
Request JSON Object
  • biller_client_id (string) – biller_client_id(string) In the current version, we don’t authenticate, so enter any value.

  • google_play_client_id (string) – GooglePlay client id

  • google_play_client_secret (string) – GooglePlay client secret

  • google_play_refresh_token (string) – GooglePlay refresh token

Response JSON Object
  • result (integer) –

    • 0: Success

    • 2: Already authenticated session

    • 3: Incorrect ID

    • 4: Incorrect authentication key

    • 5: Incorrect service provider

    • 6: Incorrect authentication parameters for the service provider (Google)

    • 2000: Parameter error

    • other: Other errors

  • description (string) – Description of error

  • sessionid (string) – Session ID, to be sent with the receipt validation request.

Google Play receipt authentication

POST /v1/validation/googleplay
Request JSON Object
  • sessionid (string) – Session ID, received from authentication

  • player_id (string) – Empty string, or the player ID to be saved in the the biller database

  • player_service_provider (string) – Empty string, or the player service provider to be saved in the biller database

  • package_name (string) – Google Play package name, acquired from the Google Play client SDK

  • product_id (string) – Google Play product id, acquired from the Google Play client SDK

  • purchase_token (string) – Google Play purchase token, acquired from the Google Play client SDK

Response JSON Object
  • result (integer) –

    • 0: success

    • 1000: duplicate receipt

    • 1001: incorrect receipt

    • 1002: incorrect service provider

    • 1003: unauthenticated service provider

    • 1004: cancelled receipt

    • 1005: unauthorized session

    • 2000: parameter error

    • other: other errors

Apple AppStore receipt validation URI /v1/validation/appleappstore

POST /v1/validation/appleappstore
Request JSON Object
  • sessionid (string) – Session ID, received from authentication

  • player_id (string) – Empty string, or the player ID to be saved in the biller database

  • player_service_provider (string) – Empty string, or the player service provider to be saved in the biller database

  • receipt_data (string) – AppStore receipt data, acquired from the Apple AppStore client SDK

  • product_id (string) – AppStore product ID, acquired from the Apple AppStore client SDK

  • quantity (string) – quantity, acquired from the Apple AppStore client SDK

Response JSON Object
  • result (integer) –

    • 0: success

    • 1000: duplicate receipt

    • 1001: incorrect receipt

    • 1002: incorrect service provider

    • 1003: unauthenticated service provider

    • 1005: unauthorized session

    • 2000: parameter error

    • other: other errors

TStore receipt validation

POST /v1/validation/tstore
Request JSON Object
  • sessionid (string) – Session ID, received from authentication

  • player_id (string) – Empty string, or the player ID to be saved in the biller database

  • player_service_provider (string) – Empty string, or the player service provider to be saved in the biller database

  • txid (string) – TStore txid (unique valid issued by the IAP server at purchase)

  • appid (string) – TStore appid (Free-to-play parent product ID)

  • signdata (string) – TStore signdata (digital receipt data)

  • products (string[]) – TStore product id(string) array

  • test (boolean) – true if using the test (for development) TStore IAP server

Response JSON Object
  • result (integer) –

    • 0: success

    • 1000: duplicate receipt

    • 1001: incorrect receipt

    • 1002: incorrect service provider

    • 1003: unauthenticated service provider

    • 1005: unauthorized session

    • 2000: parameter error

    • other: other errors

Example

Authentication

$ telnet localhost 12811
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /v1/authentication HTTP/1.1
Content-Length: 27

{"biller_client_id":"test"}
HTTP/1.1 200 OK
content-length: 85
content-type: application/json

{"result": 0, "description": "", "sessionid": "f43fd097-7c64-49a9-8edb-efeb0969d05f"}

Apple Appstore Receipt Validation

$ telnet localhost 12811
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /v1/validation/appleappstore HTTP/1.1
Content-Length: xxx

{"sessionid":"f43fd097-7c64-49a9-8edb-efeb0969d05f", "quantity": 1, "product_id":"xxx", "receipt_data":"xxx"}
HTTP/1.1 200 OK
content-length: 13
content-type: application/json

{"result": 0}