In a word game hosted as Canvas app at Facebook I would like to sell a consumable “1-year VIP status” giving players temporary access to certain areas in the game – by using the Facebook Payments Lite (serverless).
My JavaScript code displays Pay Dialog and then passes signed_request to my PHP-script –
JavaScript code at my Canvas app:
function buyVip() {
var obj = {
method: "pay",
action: "purchaseiap",
product_id: "test1"
};
FB.ui(obj, function(data) {
$.post("/payment-lite.php",
{ signed_request: data.signed_request })
.done(function(data) {
location.reload();
});
});
}
My PHP script /payment-lite.php:
const APP_SECRET = 'XXXXXXX';
$request = parse_signed_request($_POST['signed_request'], APP_SECRET);
error_log(print_r($request, TRUE));
// TODO validate $request and set the user VIP status in the game database
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), TRUE);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return NULL;
}
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = TRUE);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return NULL;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
In the app Dashboard -> Web Payments I have added a test user and a test product with “Product ID” test1
and the price of EUR 0.01:
Finally I login as test user and press a button in the app calling the buyVip
method – causing the Pay Dialog to appear:
Then in the server logs I see the payment.php
script being called successfully:
[30-Jul-2017 14:34:20 Europe/Berlin] Array
(
[algorithm] => HMAC-SHA256
[amount] => 0.01
[app_id] => 376218039240910
[currency] => EUR
[issued_at] => 1501418059
[payment_id] => 1084810821649513
[product_id] => test1
[purchase_time] => 1501418057
[purchase_token] => 498440660497153
[quantity] => 1
[status] => completed
)
However when I try the same procedure later, the Pay Dialog appears, but then fails after pressing the Buy button with the error
There Was a Problem Processing Your Payment: Sorry, but we’re having
trouble processing your payment. You have not been charged for this
transaction. Please try again.
And in the browser console I see the 1383001 Unknown error code:
{error_code: 1383001, error_message: “There Was a Problem Processing
Your Payment: Sorry…n charged for this transaction. Please try
again.”}
What does it mean please, why do first buy requests succeed, but the subsequent fail?
In my app I am of course going to hide the “buy VIP status” button for a year after successful purchase, but still I would like to know, what is happening here.
Also in the future I would like to sell consumable virtual goods like “coins” in my game and then multiple purchases should succeed.
UPDATE:
I have tried to consume the purchase by adding the following code to my payment.php
(using APP_ID|APP_SECRET instead of the required user access token):
$post = [
'access_token' => APP_ID . '|' . APP_SECRET,
];
$ch = curl_init('https://graph.facebook.com/498440660497153/consume');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$response = curl_exec($ch);
curl_close($ch);
error_log(print_r($response, TRUE));
But unfortunately get the error:
{“error”:{“message”:”Unsupported post request. Object with ID
‘498440660497153’ does not exist, cannot be loaded due to missing
permissions, or does not support this operation. Please read the Graph
API documentation at
https://developers.facebook.com/docs/graph-api”,”type”:”GraphMethodException”,”code”:100,”fbtrace_id”:”HDusTBubydJ”}}
2
Answers
I am answering my own question to share the complete source code needed to sell consumable virtual goods via Facebook Payments Lite, based on Alexey Mukhin's helpful reply -
JavaScript code in your Facebook Canvas app (assign to a button-ONCLICK):
PHP code in the payment-lite.php script hosted at your web server:
NOTE: If you happen to have a recent PHP version, then better use hash_equals in the above code, to mitigate timing attacks.
Do not forget to enable Payments Lite in the Facebook Dashboard of your app and also add a "test1" product there:
If you follow the above instructions you will be able to purchase the "test1" item multiple times and the output you will get in the PHP logs will look like:
Finally, I will share below my webhook code for non-lite Facebook Payments, because that is what I actually have ended up using (it handles chargebacks and does not need to mark items consumable after purchases) -
JavaScript code in your Facebook Canvas app (assign to a button-ONCLICK):
PHP code in the payment-full.php script hosted at your web server:
Do not forget to disable Payments Lite in the Facebook Dashboard of your app and also add the "payment-full.php" webhook there:
Finally add the "test1.html" product file at your web server:
There are currently not many Facebook Payment examples to be discovered on the web.
So upvote the question and the answer, if you have found my source code (public domain license) useful, to help other developers to discover it.
You should consume previous purchase for that user before creating new one with same product_id. This is done to prevent users from buying the same item more than once for non consumable item.
https://developers.facebook.com/docs/games_payments/payments_lite#consuming
UPDATE:
If you want to consume purchase via server you can pass access_token to your php script.
To get access_token you can use this.