[英]Facebook iOS SDK Login Fails On First Attempt but Successful on Second
[英]Payments Lite (serverless): first purchase works, but the second always fails
在Facebook上作為Canvas應用托管的文字游戲中,我想出售一種消耗性的“ 1年VIP狀態”,通過使用Facebook Payments Lite(無服務器)讓玩家臨時進入游戲中的某些區域。
我的JavaScript代碼顯示“ 付款對話框” ,然后將signed_request傳遞給我的PHP腳本-
我的Canvas應用中的JavaScript代碼:
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();
});
});
}
我的PHP腳本/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, '-_', '+/'));
}
在應用程序儀表板-> Web Payments中,我添加了一個測試用戶和一個帶有“產品ID” test1
且價格為0.01歐元的測試產品:
最終,我以測試用戶身份登錄,然后在應用程序中按一個調用buyVip
方法的按鈕-導致出現“ 付款”對話框 :
然后在服務器日志中,我看到payment.php
腳本被成功調用:
[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
)
但是,當我稍后嘗試相同的過程時,出現“ 付款”對話框 ,但在按“ 購買”按鈕后出現錯誤,然后失敗
處理您的付款時出現問題:抱歉,我們無法處理您的付款。 您尚未為此交易付費。 請再試一次。
在瀏覽器控制台中,我看到1383001未知錯誤代碼:
{error_code:1383001,error_message:“處理您的付款時出現問題:很抱歉,此交易已收取n費用。請重試。”}
請問是什么意思,為什么第一次購買請求成功,但是隨后失敗?
在我的應用程序中,我當然會在成功購買后隱藏“購買VIP身份”按鈕一年,但是我仍然想知道這里發生了什么。
同樣,將來我想在游戲中出售諸如“硬幣”之類的消耗性虛擬商品,然后多次購買應會成功。
更新:
我嘗試通過將以下代碼添加到我的payment.php
(使用APP_ID | APP_SECRET代替所需的用戶訪問令牌)來消耗購買的商品:
$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));
但不幸的是得到了錯誤:
{“ error”:{“ message”:“不支持的發布請求。ID為'498440660497153'的對象不存在,由於權限丟失而無法加載,或不支持此操作。請閱讀Graph API文檔,網址為https:// /developers.facebook.com/docs/graph-api“,”類型“:” GraphMethodException“,”代碼“:100,” fbtrace_id“:” HDusTBubydJ“}}
在創建具有相同product_id的新商品之前,您應該為該用戶使用以前的購買商品。 這樣做是為了防止用戶為非消耗性物品多次購買同一物品。
FB.api(
'/' + PURCHASE_TOKEN + '/consume', // Replace the PURCHASE_TOKEN
'post',
{access_token: access_token}, // Replace with a user access token
result => {
console.log('consuming product', productId, 'with purchase token', purchaseToken);
console.log('Result:');
console.log(result);
}
);
https://developers.facebook.com/docs/games_payments/payments_lite#using
更新:
如果您想通過服務器消費,可以將access_token傳遞給您的php腳本。
$.post("/words/facebook/payment.php", { access_token: access_token })
要獲取access_token,您可以使用它。
var access_token = '';
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
access_token = response.authResponse.accessToken;
}
});
我正在回答自己的問題,以根據Alexey Mukhin的有用答復分享通過Facebook Payments Lite出售消費性虛擬商品所需的完整源代碼-
您的Facebook Canvas應用中的JavaScript代碼(分配給按鈕-ONCLICK):
function buyItemLite() {
var payDialog = {
method: "pay",
action: "purchaseiap",
product_id: "test1"
};
FB.ui(payDialog, function(payResponse) {
FB.getLoginStatus(function(loginResponse) {
if (loginResponse.status === "connected") {
$.post("/payment-lite.php", {
signed_request: payResponse.signed_request,
access_token: loginResponse.authResponse.accessToken
})
.done(function(consumeResponse) {
location.reload();
});
}
});
});
}
Web服務器上托管的payment-lite.php腳本中的PHP代碼:
const APP_ID = 'replace by your app id';
const APP_SECRET = 'replace by your app secret';
const SIGNED_REQUEST = 'signed_request';
const STATUS = 'status';
const COMPLETED = 'completed';
const PRODUCT_ID = 'product_id';
const PURCHASE_TOKEN = 'purchase_token';
const ACCESS_TOKEN = 'access_token';
const CONSUME_URL = 'https://graph.facebook.com/%d/consume';
$request = parse_signed_request($_REQUEST[SIGNED_REQUEST], APP_SECRET);
error_log('pay dialog request: ' . print_r($request, TRUE));
if ($request[STATUS] === COMPLETED && $request[PRODUCT_ID] === 'test1') {
# perform POST request to consume the purchase_token
$url = sprintf(CONSUME_URL, $request[PURCHASE_TOKEN]);
$fields = array(ACCESS_TOKEN => $_REQUEST[ACCESS_TOKEN]);
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_POSTFIELDS, $fields);
$response = curl_exec($client);
curl_close($client);
error_log('consume response: ' . print_r($response, TRUE));
# TODO give the player the newly purchased consumable "test1" product
}
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) { // or better use hash_equals
error_log('Bad Signed JSON signature!');
return NULL;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
注意:如果您碰巧具有最新的PHP版本,則最好在上面的代碼中使用hash_equals ,以減輕計時攻擊 。
不要忘記在應用程序的Facebook儀表板中啟用Payments Lite,並在其中添加“ test1”產品:
如果您按照上述說明進行操作,則可以多次購買“ test1”項目,並且您在PHP日志中獲得的輸出將類似於:
pay dialog request: Array
(
[algorithm] => HMAC-SHA256
[amount] => 0.01
[app_id] => 376218039240910
[currency] => EUR
[issued_at] => 1501674845
[payment_id] => 1041009052696057
[product_id] => test1
[purchase_time] => 1501674843
[purchase_token] => 499658830375336
[quantity] => 1
[status] => completed
)
consume response: {"success":true}
最后,我將在我的webhook代碼下面分享非輕型Facebook Payments ,因為這實際上是我最終使用的代碼(它可以處理退款,並且不需要在購買后標記消耗品 )-
您的Facebook Canvas應用中的JavaScript代碼(分配給按鈕-ONCLICK):
function buyItemFull() {
var payDialog = {
method: "pay",
action: "purchaseitem",
product: "https://myserver/test1.html"
};
FB.ui(payDialog, function(data) {
location.reload();
});
}
Web服務器上托管的payment-full.php腳本中的PHP代碼:
const APP_ID = 'replace by your app id';
const APP_SECRET = 'replace by your app secret';
const HUB_MODE = 'hub_mode';
const HUB_CHALLENGE = 'hub_challenge';
const HUB_VERIFY_TOKEN = 'hub_verify_token';
const SUBSCRIBE = 'subscribe';
const ENTRY = 'entry';
const CHANGED_FIELDS = 'changed_fields';
const ID = 'id';
const USER = 'user';
const ACTIONS = 'actions';
const ITEMS = 'items';
const PRODUCT = 'product';
const AMOUNT = 'amount';
# payment status can be initiated, failed, completed
const STATUS = 'status';
const COMPLETED = 'completed';
# possible payment event types are listed below
const TYPE = 'type';
const CHARGE = 'charge';
const CHARGEBACK_REVERSAL = 'chargeback_reversal';
const REFUND = 'refund';
const CHARGEBACK = 'chargeback';
const DECLINE = 'decline';
const GRAPH = 'https://graph.facebook.com/v2.10/%d?access_token=%s|%s&fields=user,actions,items';
const TEST1 = 'https://myserver/test1.html';
# called by Facebook Dashboard when "Test Callback URL" button is pressed
if (isset($_GET[HUB_MODE]) && $_GET[HUB_MODE] === SUBSCRIBE) {
print($_GET[HUB_CHALLENGE]);
exit(0);
}
# called when there is an update on a payment (NOTE: better use hash_equals)
$body = file_get_contents('php://input');
if ('sha1=' . hash_hmac('sha1', $body, APP_SECRET) != $_SERVER['HTTP_X_HUB_SIGNATURE']) {
error_log('payment sig=' . $_SERVER['HTTP_X_HUB_SIGNATURE'] . ' does not match body=' . $body);
exit(1);
}
# find the updated payment id and what has changed: actions or disputes
$update = json_decode($body, TRUE);
error_log('payment update=' . print_r($update, TRUE));
$entry = array_shift($update[ENTRY]);
$payment_id = $entry[ID];
$changed_fields = $entry[CHANGED_FIELDS];
if (!in_array(ACTIONS, $changed_fields)) {
error_log('payment actions has not changed');
exit(0);
}
# fetch the updated payment details: user, actions, items
$graph = sprintf(GRAPH, $payment_id, APP_ID, APP_SECRET);
$payment = json_decode(file_get_contents($graph), TRUE);
error_log('payment details=' . print_r($payment, TRUE));
# find the user id who has paid
$uid = $payment[USER][ID];
# find the last action and its status and type
$actions = $payment[ACTIONS];
$action = array_pop($actions);
$status = $action[STATUS];
$type = $action[TYPE];
$price = $action[AMOUNT];
# find which product was purchased
$items = $payment[ITEMS];
$item = array_pop($items);
$product = $item[PRODUCT];
error_log("payment uid=$uid status=$status type=$type product=$product price=$price");
if ($status != COMPLETED) {
error_log('payment status is not completed');
exit(0);
}
# money has been received, update the player record in the database
if ($type === CHARGE || $type === CHARGEBACK_REVERSAL) {
if ($product === TEST1) {
# TODO give the player the purchased "test1" product
}
} else if ($type === REFUND || $type === CHARGEBACK || $type === DECLINE) {
# TODO take away from the player the "test1" product
}
不要忘記在應用程序的Facebook儀表板中禁用Payments Lite ,並在此處添加“ payment-full.php” webhook:
最后,在您的Web服務器上添加“ test1.html”產品文件:
<!DOCTYPE html><html>
<head prefix=
"og: http://ogp.me/ns#
fb: http://ogp.me/ns/fb#
product: http://ogp.me/ns/product#">
<meta property="og:type" content="og:product" />
<meta property="og:title" content="Test1" />
<meta property="og:image" content="https://myserver/icon-50x50.png" />
<meta property="og:description" content="Test1" />
<meta property="og:url" content="https://myserver/test1.html" />
<meta property="product:price:amount" content="0.01"/>
<meta property="product:price:currency" content="EUR"/>
</head>
</html>
當前在網絡上沒有很多Facebook付款示例。
因此,如果您發現我的源代碼(公共領域許可)很有用,請對問題和答案進行投票,以幫助其他開發人員發現它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.