Skip to content

Payment

The logic for player payments within the game involves players using Lobah APP currency to purchase in-game items.

1. Configure Products

Go to the Lobah Developer Center Operations -> Products page, click Add New Product, and fill in the following information:

  • Product name: Name of the product
  • Product identification: Unique identifier for the product. This is used to identify products within the game; please ensure it is distinct.
  • Product price: Price of the product, in Lobah currency Coins. The ratio of Coins to USD is 100:1.

2. Obtain App ID and App Key

Go to the Lobah Developer Center Deployment -> Configuration page to obtain the App ID and App Key.

3. Server API Validation and Payment

The Lobah Lobah Server API endpoint is: https://game-gateway.lobah.net

The first step in using the Server API is to perform a signature algorithm, then call the Purchase interface to make the payment. For more details, refer to the Authentication section in the API Reference documentation.

4. Sample Code

The following are code examples for calling the Server API in programming languages such as Java, PHP, Go, and NodeJS (server-side):

4.1 Java

java


import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

public class Demo {

    private static final String appKey = "";       // Configuration -> appKey
    private static final String accessToken = "";  // params.token 
    private static final String appId = "";        // params.gameId 
    private static final String sessionId = "";    // params.sessionId 
    private static final String uid = "";          // params.uid
    private static final String productId = "";    // Product Identification
    
    private static final String method = "POST";
    private static final String host = "https://game-gateway.lobah.net";
    
    public static void main(String[] args) throws Exception {
        test();
        getProfile();
        purchase();
    }

    public static void test() throws Exception {
        String path = "/1.0/open-gateway/game/test";
        String postBody = "";
        httpPost(path, postBody);
    }

    public static void purchase() throws Exception {
        String referenceId = UUID.randomUUID().toString().replace("-", "").substring(0, 12);
        String path = "/1.0/open-gateway/game/purchase";
        String postBody = "{ \"app_id\": " + appId + ", " + " \"product_id\": \"" + productId + "\", \"reference_id\": \"" + referenceId + "\", \"session_id\": " + sessionId + ", \"uid\": " + uid + " }";
        httpPost(path, postBody);
    }

    public static void getProfile() throws Exception {
        String path = "/1.0/open-gateway/game/get-profile";
        String postBody = "{\"app_id\": " + appId + ", \"token\": \"" + accessToken + "\", \"uid\": " + uid + " }";
        httpPost(path, postBody);
    }

    public static void httpPost(String requestPath, String postBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        Map<String, String> reqParamsMap = new TreeMap<>();
        reqParamsMap.put("access_token", accessToken);
        reqParamsMap.put("app_id", appId);
        reqParamsMap.put("nonce", UUID.randomUUID().toString().replace("-", "").substring(0, 8));
        reqParamsMap.put("ts", String.valueOf(Instant.now().getEpochSecond()));
        reqParamsMap.put("uid", uid);
        reqParamsMap.put("zone", "SA");

        String reqParam = encodeURL(reqParamsMap);
        String encodeStr = method + requestPath + reqParam + postBody;
        String urlEncodedData = URLEncoder.encode(encodeStr, "UTF-8");
        String digest = genDigest(urlEncodedData, appKey, "HmacMD5");
        System.out.println("Encode String: " + encodeStr);
        System.out.println("Encode Data: " + urlEncodedData);
        System.out.println("Digest: " + digest);
        httpPostWithSig(host + requestPath + "?" +  reqParam + "&sig=" + digest, postBody);
    }

    private static void httpPostWithSig(String requestUrl, String requestBody) throws IOException {
        System.out.println("Request Url: " + requestUrl);
        URL url = new URL(requestUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod(method);
        connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
        connection.setRequestProperty("Content-Type", "application/json; utf-8");
        connection.setRequestProperty("Accept", "application/json");
        connection.setDoOutput(true);

        try (OutputStream os = connection.getOutputStream()) {
            byte[] input = requestBody.getBytes(StandardCharsets.UTF_8);
            os.write(input, 0, input.length);
        }

        int responseCode = connection.getResponseCode();
        System.out.println("Response Code: " + responseCode);

        try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String responseLine;
            while ((responseLine = br.readLine()) != null) {
                response.append(responseLine.trim());
            }
            System.out.println("Response Body: " + response);
        }
        connection.disconnect();
    }

    private static String genDigest(String msg, String keyString, String algo) throws NoSuchAlgorithmException, InvalidKeyException {
        SecretKeySpec key = new SecretKeySpec((keyString).getBytes(StandardCharsets.UTF_8), algo);
        Mac mac = Mac.getInstance(algo);
        mac.init(key);
        byte[] bytes = mac.doFinal(msg.getBytes(StandardCharsets.US_ASCII));
        StringBuilder hash = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(0xFF & aByte);
            if (hex.length() == 1) {
                hash.append('0');
            }
            hash.append(hex);
        }
        return hash.toString();
    }

    private static String encodeURL(Map<String, String> parameters) {
        StringBuilder result = new StringBuilder(2048);
        for (String encodedName : parameters.keySet()) {
            String encodedValue = parameters.get(encodedName);
            if (result.length() > 0) {
                result.append('&');
            }
            result.append(encodedName);
            if (encodedValue != null) {
                result.append("=");
                result.append(encodedValue);
            }
        }
        return result.toString();
    }
}

4.2 PHP

php

<?php

// Usage example
function main() {
    $client = new GameAPIClient();

    // Set configuration (replace with your actual configuration)
    $client->setConfig(
        '',  // Configuration -> appKey
        '',  // params.token
        '',  // params.gameId 
        '',  // params.sessionId
        '',  // params.uid
        ''   // Product Identification
    );

    try {
        echo "=== Test API ===\n";
        $client->test();

        echo "\n=== Get User Profile ===\n";
        $client->getProfile();

        echo "\n=== Purchase Product ===\n";
        $client->purchase();

    } catch (Exception $e) {
        echo "Execution failed: " . $e->getMessage() . "\n";
    }
}

class GameAPIClient {
    private $config;
    private $host = 'https://game-gateway.lobah.net';
    private $method = 'POST';
    private $zone = 'SA';

    public function __construct($config = []) {
        $this->config = array_merge([
            'appKey' => '',
            'accessToken' => '',
            'appId' => '',
            'sessionId' => '',
            'uid' => '',
            'productId' => ''
        ], $config);
    }

    public function setConfig($appKey, $accessToken, $appId, $sessionId, $uid, $productId) {
        $this->config = [
            'appKey' => $appKey,
            'accessToken' => $accessToken,
            'appId' => $appId,
            'sessionId' => $sessionId,
            'uid' => $uid,
            'productId' => $productId
        ];
    }

    public function test() {
        $path = '/1.0/open-gateway/game/test';
        $postBody = '';
        return $this->httpPost($path, $postBody);
    }

    public function purchase() {
        $referenceId = $this->generateReferenceID();
        $path = '/1.0/open-gateway/game/purchase';
        
        $postBody = json_encode([
            'app_id' => $this->config['appId'],
            'product_id' => $this->config['productId'],
            'reference_id' => $referenceId,
            'session_id' => $this->config['sessionId'],
            'uid' => $this->config['uid']
        ]);

        return $this->httpPost($path, $postBody);
    }

    public function getProfile() {
        $path = '/1.0/open-gateway/game/get-profile';
        
        $postBody = json_encode([
            'app_id' => $this->config['appId'],
            'token' => $this->config['accessToken'],
            'uid' => $this->config['uid']
        ]);

        return $this->httpPost($path, $postBody);
    }

    private function httpPost($requestPath, $postBody) {
        // Build request parameters
        $reqParams = [
            'access_token' => $this->config['accessToken'],
            'app_id' => $this->config['appId'],
            'nonce' => $this->generateNonce(),
            'ts' => (string) time(),
            'uid' => $this->config['uid'],
            'zone' => $this->zone
        ];

        // Sort parameters
        ksort($reqParams);
        $reqParam = http_build_query($reqParams, '', '&');

        // Build signature string
        $encodeStr = $this->method . $requestPath . $reqParam . $postBody;
        $urlEncodedData = urlencode($encodeStr);
        $digest = $this->genDigest($urlEncodedData, $this->config['appKey'], 'md5');

        echo "Encode String: " . $encodeStr . "\n";
        echo "Encode Data: " . $urlEncodedData . "\n";
        echo "Digest: " . $digest . "\n";

        $finalURL = $this->host . $requestPath . '?' . $reqParam . '&sig=' . $digest;
        return $this->httpPostWithSig($finalURL, $postBody);
    }

    private function httpPostWithSig($requestUrl, $requestBody) {
        echo "Request Url: " . $requestUrl . "\n";

        $ch = curl_init();
        
        curl_setopt_array($ch, [
            CURLOPT_URL => $requestUrl,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $requestBody,
            CURLOPT_HTTPHEADER => [
                'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
                'Content-Type: application/json; utf-8',
                'Accept: application/json',
                'Content-Length: ' . strlen($requestBody)
            ],
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        if (curl_errno($ch)) {
            $error = curl_error($ch);
            curl_close($ch);
            throw new Exception("CURL Error: " . $error);
        }
        
        curl_close($ch);

        echo "Response Code: " . $httpCode . "\n";
        echo "Response Body: " . $response . "\n";
        
        return [
            'statusCode' => $httpCode,
            'body' => $response
        ];
    }

    private function genDigest($msg, $key, $algo) {
        if ($algo === 'md5') {
            return hash_hmac('md5', $msg, $key);
        }
        return hash($algo, $msg);
    }

    private function generateNonce() {
        $chars = '0123456789abcdef';
        $result = '';
        for ($i = 0; $i < 8; $i++) {
            $result .= $chars[rand(0, strlen($chars) - 1)];
        }
        return $result;
    }

    private function generateReferenceID() {
        $chars = '0123456789abcdef';
        $result = '';
        for ($i = 0; $i < 12; $i++) {
            $result .= $chars[rand(0, strlen($chars) - 1)];
        }
        return $result;
    }

    private function urlencode($str) {
        return urlencode($str);
    }
}


// Run this file directly
if (basename(__FILE__) === basename($_SERVER['PHP_SELF'])) {
    main();
}

// Return client instance (for other files to use)
return new GameAPIClient();

4.3 Go

go

package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

// Config configuration information
type Config struct {
	AppKey      string
	AccessToken string
	AppID       string
	SessionID   string
	UID         string
	ProductID   string
	Host        string
	Method      string
	Zone        string
}

// Client API client
type Client struct {
	config *Config
	client *http.Client
}

// Usage example
func main() {
	client := NewClient()

	// Set configuration (replace with your actual configuration)
	client.SetConfig(
		"", // Configuration -> appKey
		"", // params.token
		"", // params.gameId
		"", // params.sessionId
		"", // params.uid
		"", // Product Identification
	)

	// Run tests
	fmt.Println("=== Test API ===")
	if err := client.Test(); err != nil {
		fmt.Printf("Test failed: %v\n", err)
	}

	fmt.Println("\n=== Get User Profile ===")
	if err := client.GetProfile(); err != nil {
		fmt.Printf("Get user profile failed: %v\n", err)
	}

	fmt.Println("\n=== Purchase Product ===")
	if err := client.Purchase(); err != nil {
		fmt.Printf("Purchase failed: %v\n", err)
	}
}

// NewClient create new API client
func NewClient() *Client {
	return &Client{
		config: &Config{
			AppKey:      "", // Configuration -> appKey
			AccessToken: "", // params.token
			AppID:       "", // params.gameId
			SessionID:   "", // params.sessionId
			UID:         "", // params.uid
			ProductID:   "", // Product Identification
			Host:        "https://game-gateway.lobah.net",
			Method:      "POST",
			Zone:        "SA",
		},
		client: &http.Client{Timeout: 30 * time.Second},
	}
}

// SetConfig set configuration
func (c *Client) SetConfig(appKey, accessToken, appID, sessionID, uid, productID string) {
	c.config.AppKey = appKey
	c.config.AccessToken = accessToken
	c.config.AppID = appID
	c.config.SessionID = sessionID
	c.config.UID = uid
	c.config.ProductID = productID
}

// Test test API
func (c *Client) Test() error {
	path := "/1.0/open-gateway/game/test"
	postBody := ""
	return c.httpPost(path, postBody)
}

// Purchase purchase product
func (c *Client) Purchase() error {
	referenceID := generateReferenceID()
	path := "/1.0/open-gateway/game/purchase"

	requestBody := map[string]interface{}{
		"app_id":       c.config.AppID,
		"product_id":   c.config.ProductID,
		"reference_id": referenceID,
		"session_id":   c.config.SessionID,
		"uid":          c.config.UID,
	}

	jsonBody, _ := json.Marshal(requestBody)
	return c.httpPost(path, string(jsonBody))
}

// GetProfile get user profile
func (c *Client) GetProfile() error {
	path := "/1.0/open-gateway/game/get-profile"

	requestBody := map[string]interface{}{
		"app_id": c.config.AppID,
		"token":  c.config.AccessToken,
		"uid":    c.config.UID,
	}

	jsonBody, _ := json.Marshal(requestBody)
	return c.httpPost(path, string(jsonBody))
}

// httpPost send HTTP POST request
func (c *Client) httpPost(requestPath string, postBody string) error {
	// Build request parameters
	reqParams := map[string]string{
		"access_token": c.config.AccessToken,
		"app_id":       c.config.AppID,
		"nonce":        generateNonce(),
		"ts":           fmt.Sprintf("%d", time.Now().Unix()),
		"uid":          c.config.UID,
		"zone":         c.config.Zone,
	}

	// Sort parameters
	keys := make([]string, 0, len(reqParams))
	for k := range reqParams {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// Build parameter string
	var paramBuilder strings.Builder
	for i, k := range keys {
		if i > 0 {
			paramBuilder.WriteString("&")
		}
		paramBuilder.WriteString(k)
		paramBuilder.WriteString("=")
		paramBuilder.WriteString(reqParams[k])
	}
	reqParam := paramBuilder.String()

	// Build signature string
	encodeStr := c.config.Method + requestPath + reqParam + postBody
	urlEncodedData := url.QueryEscape(encodeStr)
	digest := genDigest(urlEncodedData, c.config.AppKey, "HmacMD5")

	fmt.Printf("Encode String: %s\n", encodeStr)
	fmt.Printf("Encode Data: %s\n", urlEncodedData)
	fmt.Printf("Digest: %s\n", digest)

	// Build final URL
	finalURL := fmt.Sprintf("%s%s?%s&sig=%s", c.config.Host, requestPath, reqParam, digest)

	return c.httpPostWithSig(finalURL, postBody)
}

// httpPostWithSig send HTTP request with signature
func (c *Client) httpPostWithSig(requestURL string, requestBody string) error {
	fmt.Printf("Request Url: %s\n", requestURL)

	req, err := http.NewRequest(c.config.Method, requestURL, bytes.NewBufferString(requestBody))
	if err != nil {
		return fmt.Errorf("create request failed: %v", err)
	}

	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
	req.Header.Set("Content-Type", "application/json; utf-8")
	req.Header.Set("Accept", "application/json")

	resp, err := c.client.Do(req)
	if err != nil {
		return fmt.Errorf("send request failed: %v", err)
	}
	defer resp.Body.Close()

	fmt.Printf("Response Code: %d\n", resp.StatusCode)

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("read response failed: %v", err)
	}

	fmt.Printf("Response Body: %s\n", string(body))
	return nil
}

// genDigest generate HMAC signature
func genDigest(msg string, key string, algo string) string {
	mac := hmac.New(md5.New, []byte(key))
	mac.Write([]byte(msg))
	return hex.EncodeToString(mac.Sum(nil))
}

// generateNonce generate random nonce
func generateNonce() string {
	const charset = "0123456789abcdef"
	b := make([]byte, 8)
	for i := range b {
		b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
	}
	return string(b)
}

// generateReferenceID generate reference_id
func generateReferenceID() string {
	const charset = "0123456789abcdef"
	b := make([]byte, 12)
	for i := range b {
		b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
	}
	return string(b)
}

4.4 NodeJS

js

const crypto = require('crypto');
const https = require('https');

// Usage example
async function main() {
    const client = new GameAPIClient(config);

    // Set configuration (replace with your actual configuration)
    client.setConfig(
        '',    // Configuration -> appKey
        '',    // params.token
        '',    // params.gameId
        '',    // params.sessionId
        '',    // params.uid
        ''     // Product Identification
    );

    try {
        console.log('=== Test API ===');
        await client.test();

        console.log('\n=== Get User Profile ===');
        await client.getProfile();

        console.log('\n=== Purchase Product ===');
        await client.purchase();

    } catch (error) {
        console.error('Execution failed:', error);
    }
}


// Configuration information
const config = {
    appKey: '',       // Configuration -> appKey
    accessToken: '',  // params.token 
    appId: '',        // params.gameId 
    sessionId: '',    // params.sessionId 
    uid: '',          // params.uid
    productId: '',    // Product Identification
    method: 'POST',
    host: 'game-gateway.lobah.net',
    zone: 'SA'
};

// API client class
class GameAPIClient {
    constructor(config = {}) {
        this.config = { ...config };
    }

    // Set configuration
    setConfig(appKey, accessToken, appId, sessionId, uid, productId) {
        this.config.appKey = appKey;
        this.config.accessToken = accessToken;
        this.config.appId = appId;
        this.config.sessionId = sessionId;
        this.config.uid = uid;
        this.config.productId = productId;
    }

    // Test API
    async test() {
        const path = '/1.0/open-gateway/game/test';
        const postBody = '';
        return await this.httpPost(path, postBody);
    }

    // Purchase product
    async purchase() {
        const referenceId = this.generateReferenceID();
        const path = '/1.0/open-gateway/game/purchase';
        
        const postBody = JSON.stringify({
            app_id: this.config.appId,
            product_id: this.config.productId,
            reference_id: referenceId,
            session_id: this.config.sessionId,
            uid: this.config.uid
        });

        return await this.httpPost(path, postBody);
    }

    // Get user profile
    async getProfile() {
        const path = '/1.0/open-gateway/game/get-profile';
        
        const postBody = JSON.stringify({
            app_id: this.config.appId,
            token: this.config.accessToken,
            uid: this.config.uid
        });

        return await this.httpPost(path, postBody);
    }

    // HTTP POST request
    async httpPost(requestPath, postBody) {
        // Build request parameters
        const reqParams = {
            access_token: this.config.accessToken,
            app_id: this.config.appId,
            nonce: this.generateNonce(),
            ts: Math.floor(Date.now() / 1000).toString(),
            uid: this.config.uid,
            zone: this.config.zone
        };

        // Sort parameters
        const sortedKeys = Object.keys(reqParams).sort();
        const paramPairs = [];
        
        for (const key of sortedKeys) {
            paramPairs.push(`${key}=${reqParams[key]}`);
        }
        
        const reqParam = paramPairs.join('&');

        // Build signature string
        const encodeStr = this.config.method + requestPath + reqParam + postBody;
        const urlEncodedData = encodeURIComponent(encodeStr);
        const digest = this.genDigest(urlEncodedData, this.config.appKey, 'md5');

        console.log('Encode String:', encodeStr);
        console.log('Encode Data:', urlEncodedData);
        console.log('Digest:', digest);

        // Build final URL
        const finalURL = `https://${this.config.host}${requestPath}?${reqParam}&sig=${digest}`;
        
        return await this.httpPostWithSig(finalURL, postBody);
    }

    // Send signed HTTP request
    async httpPostWithSig(requestURL, requestBody) {
        console.log('Request Url:', requestURL);

        const url = new URL(requestURL);
        
        const options = {
            hostname: url.hostname,
            port: url.port || 443,
            path: url.pathname + url.search,
            method: this.config.method,
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
                'Content-Type': 'application/json; utf-8',
                'Accept': 'application/json',
                'Content-Length': Buffer.byteLength(requestBody)
            }
        };

        return new Promise((resolve, reject) => {
            const req = https.request(options, (res) => {
                console.log('Response Code:', res.statusCode);

                let responseData = '';
                
                res.on('data', (chunk) => {
                    responseData += chunk;
                });

                res.on('end', () => {
                    console.log('Response Body:', responseData);
                    resolve({
                        statusCode: res.statusCode,
                        body: responseData
                    });
                });
            });

            req.on('error', (error) => {
                console.error('Request failed:', error);
                reject(error);
            });

            req.write(requestBody);
            req.end();
        });
    }

    // Generate HMAC signature
    genDigest(msg, key, algo) {
        return crypto
            .createHmac(algo, key)
            .update(msg, 'ascii')
            .digest('hex');
    }

    // Generate random nonce
    generateNonce() {
        const chars = '0123456789abcdef';
        let result = '';
        for (let i = 0; i < 8; i++) {
            result += chars[Math.floor(Math.random() * chars.length)];
        }
        return result;
    }

    // Generate reference_id
    generateReferenceID() {
        const chars = '0123456789abcdef';
        let result = '';
        for (let i = 0; i < 12; i++) {
            result += chars[Math.floor(Math.random() * chars.length)];
        }
        return result;
    }
}


// If this file is run directly
if (require.main === module) {
    main();
}

// Export for use by other modules
module.exports = GameAPIClient;

Swipe & Play Endless Game Together