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
: Success2
: Already authenticated session3
: Incorrect ID4
: Incorrect authentication key5
: Incorrect service provider6
: Incorrect authentication parameters for the service provider (Google)2000
: Parameter errorother: 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
: success1000
: duplicate receipt1001
: incorrect receipt1002
: incorrect service provider1003
: unauthenticated service provider1004
: cancelled receipt1005
: unauthorized session2000
: parameter errorother: 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
: success1000
: duplicate receipt1001
: incorrect receipt1002
: incorrect service provider1003
: unauthenticated service provider1005
: unauthorized session2000
: parameter errorother: 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
: success1000
: duplicate receipt1001
: incorrect receipt1002
: incorrect service provider1003
: unauthenticated service provider1005
: unauthorized session2000
: parameter errorother: 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}