时光

时光的时光轴

寄以时间,予以文字
telegram
github
微信公众号

For unicorn number card access: Binance Pay

In addition to the commonly used blue-green modifier, there are also occasions when some non-mainland Chinese users or users who already own virtual currency need to use virtual currency for payment.

Before this, I used the TokenPay project to complete the callback for receiving virtual currency. Aside from using intermediaries like Coinbase, most virtual currency payments are similar to the previous V 免签, listening for the payment amount to determine the completion of the callback.

This method of listening for callbacks is very suitable for virtual currencies:

  • All transactions are transparent and traceable
  • No third party can hide this information
  • Most networks provide APIs for wallet record queries
  • Directly credited to the wallet

However, for someone like me who is tight on funds and just getting into virtual currencies, it took a long time to understand the concept of a wallet, let alone things like: miner fees, energy, bandwidth, consensus, POW...

My more intuitive feeling is: I incur a loss with every transaction, although it is a fixed loss, but there is one for each transaction. 😥


However, you may soon discover that internal transactions on exchanges are fee-free, and transactions between addresses from two exchanges are also free. So why not use the exchange's address for receiving payments?

The answer is: Every transaction on the blockchain requires someone to package and verify it, and their reward is called miner fees. However, exchanges do not need to charge these fees; it's not that exchanges can choose not to charge, but these assets actually exist in the exchange's account, and during the transfer, it just adjusts the numbers in the exchange's database.

However, this way, the transaction does not go on-chain, meaning others cannot see your transaction through a block explorer, and thus cannot monitor whether someone has paid you.

Fortunately, Binance is generous enough to provide Binance Pay for merchants, so there is a way to handle callbacks for transactions that do not go on-chain. The following content is not an advertisement, just pure praise.

  1. What is the fee for using Binance Pay?
    Completely free.

Settlement method: Based on the settlement method agreed in the contract. Currently, we only support real-time settlement.
3. Unsupported countries and regions (as of January 2023)
American Samoa, Australia, Canada (Ontario ONLY), Cuba, Guam, Indonesia, Iran, North Korea, New Zealand, Northern Mariana Islands, Philippines, Puerto Rico, Singapore, Syria, Thailand, Ukraine (Crimea Region incl. Donetsk, Luhansk, Zaporizhzhia and Kherson as well as the Crimea Peninsula), United States Minor Outlying Islands, USA, Vietnam, Virgin Islands (U.S.).
Note from the author: No China

image

Payment Process#

The official documentation provides detailed development documentation, but unfortunately, it is only available in English, so I will write more about the details I encountered.

image
The overall process is not much different from other payment merchant integrations, so let's get started.

API Requirements#

The following implementations are completed using PHP.

The official has some basic requirements for calling the API:

  • Must be HTTPS
  • Submission and response are both in JSON format
  • UTF-8 encoding
  • The encryption algorithm is HMAC-SHA512

API Address#

https://bpay.binanceapi.com

Signature Generation#

The composition is approximately: millisecond-level timestamp + \n + 32-character random string + \n + request body + \n

Then perform HMAC-SHA512 operation with the API key.

Start Integration#

Step 1, Create Order#

POST /binancepay/openapi/v2/order

The sample provided by the official is as follows:

{
  "env" : {
    "terminalType": "APP"
  },
  "orderTags": {
    "ifProfitSharing": true
  },
  "merchantTradeNo": "9825382937292",
  "orderAmount": 25.17,
  "currency": "BUSD",
  "goods" : {
    "goodsType": "01",
    "goodsCategory": "D000",
    "referenceGoodsId": "7876763A3B",
    "goodsName": "Ice Cream",
    "goodsDetail": "Greentea ice cream cone"
  }
}

In the env node, you need to specify the user's access method, such as APP, WEB, WAP, and a MINI_PROGRAM.

This mini-program should refer to the mini-program within the Binance APP; we only need to manage WEB and WAP for issuing cards.

Then there are the merchant order number, order amount (decimal, eight decimal places), currency, and product description. Note that the currency here only supports "BUSD","USDT","MBOX".

The implementation looks like this:

$data = [
                "env" => [
                    "terminalType" => $client,
                    "orderClientIp" => $this->order->buy_ip
                    ],
                "merchantTradeNo" => $this->order->order_sn,
                "orderAmount" => (float)$this->order->actual_price,
                "currency" => $this->payGateway->merchant_id,
                "goods" => [
                    "goodsType" => "02",
                    "goodsCategory" => "Z000",
                    "referenceGoodsId" => $this->order->goods_id,
                    "goodsName" => $this->purgeString($this->order->title)
                    ],
                "buyer" => [
                    "buyerEmail" => $this->order->email
                    ],
                "returnUrl" => url('detail-order-sn', ['orderSN' => $this->order->order_sn]),
                "orderExpireTime" => dujiaoka_config_get('order_expire_time', 5) * 60 * 1000,
                "webhookUrl" => url($this->payGateway->pay_handleroute . '/notify_url')
                ];

Generate Signature#

According to the official description, my implementation is as follows:

private function paramSign(array $params){
        $payload = $this->timestamp . "\n" . $this->noce . "\n" . json_encode($params) . "\n";
        $hash = hash_hmac('sha512', $payload, $this->payGateway->merchant_pem, true);
        return strtoupper(bin2hex($hash));
    }

Testing#

Unfortunately, the first error I encountered was:

Client error: `POST https://bpay.binanceapi.com/binancepay/openapi/v2/order` resulted in a `400 Bad Request` response: {"status":"FAIL","code":"403023","errorMessage":"order has expired "}

I thought I was placing an order, so why did the order expire? I started to guess whether it was related to my time zone.

It turned out not to be; it was my manually added orderExpireTime. The content should be the target time point, not the length of validity.

Fortunately, there were no other issues:

array(3) {
  ["status"]=>
  string(7) "SUCCESS"
  ["code"]=>
  string(6) "000000"
  ["data"]=>
  array(8) {
    ["prepayId"]=>
    string(18) "订单号"
    ["terminalType"]=>
    string(3) "WEB"
    ["expireTime"]=>
    int(1681103228641)
    ["qrcodeLink"]=>
    string(93) "收款二维码"
    ["qrContent"]=>
    string(63) "二维码内容"
    ["checkoutUrl"]=>
    string(68) "结账地址"
    ["deeplink"]=>
    string(148) "币安App的跳转链接"
    ["universalUrl"]=>
    string(284) "币安的UniversalUrl"
  }
}

However, with so many links provided, for convenience, I only need the checkout address, and leave the other redirects to Binance.

iShot_2023-04-10_13.07.10

Alright, the order placement is done. Next is to handle the callback.

Step 2, Handle Callback#

The official return example is:

{
  "bizType": "PAY",
  "data": "{\"merchantTradeNo\":\"xr6wYe8ATWE6thS5Sc7ezMihMFGKn6\",\"productType\":\"Food\",\"productName\":\"Ice Cream\",\"transactTime\":1619508939664,\"tradeType\":\"APP\",\"totalFee\":0.88000000,\"currency\":\"BUSD\",\"transactionId\":\"M_R_282737362839373\",\"openUserId\":\"1211HS10K81f4273ac031\",\"commission\":0,\"paymentInfo\":{\"payerId\":1000013312869,\"payMethod\":\"funding\",\"paymentInstructions\":[{\"currency\":\"BUSD\",\"amount\":0.88000000,\"price\":1}],\"channel\":\"DEFAULT\"}}",
  "bizIdStr": "29383937493038367292",
  "bizId": 29383937493038367292,
  "bizStatus": "PAY_SUCCESS"
}

So first, I need to check if the order number is valid. You can refer to the Webhook documentation.

Obtain Public Key#

Binance has used RSA encryption for payment callbacks, so we need to obtain the public key first.

It is important to note that the operation to obtain the public key is the same authentication method as APIs like placing orders. The official documentation was not very clear, and I only figured it out after trying it.
I searched the official examples and found out:
iShot_2023-04-10_17.44.21

I will directly paste the implementation code:

 private function getPublicKey($apiID){
        $endpoint = "/binancepay/openapi/certificates";
        
        $client = new Client([
		    'headers' => [ 
			    'Content-Type' => 'application/json',
			    'BinancePay-Timestamp' => $this->timestamp,
			    'BinancePay-Nonce' => $this->noce,
			    'BinancePay-Certificate-SN' => $apiID,
			    'BinancePay-Signature' => $this->paramSign([])
			]
		]);
		$response = $client->post($this->server.$endpoint, ['body' =>  json_encode([])]);
        $body = json_decode($response->getBody()->getContents(), true);
        var_dump($body);
    }

Here, you can just carry an empty array when making the request.

Verify Signature#

When verifying the signature, you must use the original data and cannot perform any operations. For example, do not parse JSON and then parse it back.

Then, the items used for signature verification are all provided by Binance, so do not mix them up.

A Few Details#

When making a payment, I suddenly found: Why is the currency unit for receiving payments not automatically converted to fiat currency?

Well, after looking around, it turns out that automatic conversion of fiat currency is not supported, so I can only implement it myself.

iShot_2023-04-10_19.19.02

Done, tested, and finished!

A Little Benefit#

Using the red envelope feature of Binance Pay, those with accounts can directly receive:

telegram-cloud-photo-size-5-6170146392937444716-y
Red envelope code BPLM61ESK4
Web version https://www.binance.com/zh-CN/my/wallet/account/payment/cryptobox

No account?
https://accounts.binance.com/register?ref=157122450
Of course, you should register through AFF 🫣

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.