一、接入流程
① App调用opensocial.requestPayment(),生成支付信息
② Mbga向对应App服务器的endpoint URL发送支付确认信息请求
③ App服务器验证mbga发送的请求之后返回response给mbga,mbga引导用户去充值页面
④ 用户同意充值了,就向App服务器发送一个支付确认的request。
⑤ App服务器验证request的正确性,处理游戏内部item购买业务,最后返回response给mbga。Mbga在收到正常的response之后,就确定了支付了。
⑥ 执行最初调用opensocial.requestPayment()的最后一个参数(执行callback 函数)
⑦ 可选的:在callback函数里面调用gadgets.io.makeRequest()来请求App服务器,用于确认支付是否正确。
⑧ App服务器调用Restful Api的Payment API来获取当前支付的状态。
⑨ Api服务器返回当前支付状态给App服务器,服务器来确认支付信息
⑩ 结果处理完毕
二、前端代码,通过JS调用,如下代码,含游戏接入代码和邀请代码
View Code
<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="bubblefish">
<Require feature="dynamic-height" />
<Require feature="opensocial-0.8" />
<Require feature="flash" />
<Require feature="opensocial-payment" />
<Link rel="payment.handler" href="{{ game_server_url }}payment/" />
</ModulePrefs>
<Content type="html" view="canvas" >
<![CDATA[
<script type="text/javascript">
function handlePaymentResponse(dataItem) {
if (dataItem.hadError()) {
//alert('got an error');
} else {
var orderId = dataItem.getData().getField(opensocial.Payment.Field.ORDER_ID);
alert('payment request accepted, orderId: ' + orderId);
}
}
function makePayment(sku_id,price) {
var itemParams = {};
itemParams[opensocial.BillingItem.Field.SKU_ID] = sku_id;
itemParams[opensocial.BillingItem.Field.PRICE] = price;
itemParams[opensocial.BillingItem.Field.COUNT] = 1;
itemParams[mbga.BillingItem.Field.NAME] = "パールで";
itemParams[mbga.BillingItem.Field.IMAGE_URL] = "http://60.29.241.40/yahoom_media/img/yabage_tu03.gif";
itemParams[opensocial.BillingItem.Field.DESCRIPTION] = '';
var item = opensocial.newBillingItem(itemParams);
var params = {};
params[opensocial.Payment.Field.ITEMS] = [item];
params[opensocial.Payment.Field.AMOUNT] = price;
var payment = opensocial.newPayment(params);
opensocial.requestPayment(payment, handlePaymentResponse);
}
function paymentPage(){
var url = "{{ game_server_url }}payment/recharge/";
var params={};
var post_data = {snsId : snsId};
params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(post_data);
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
gadgets.io.makeRequest(url, commonResponse, params);
};
var snsId = undefined;
function sendFeed(title, content){
var ap = {};
ap[opensocial.Activity.Field.TITLE] = title;//21字之内
ap[opensocial.Activity.Field.BODY] = content;//140字之内
var activity = opensocial.newActivity(ap);
opensocial.requestCreateActivity(activity, opensocial.CreateActivityPriority.LOW, function(response){
if (response.hadError()) {
var code = response.getErrorCode();
}
});
};
function invitePage(){
var url = "{{ game_server_url }}invite/isinfo/";
var params={};
var post_data = {snsId : snsId};
params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(post_data);
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
gadgets.io.makeRequest(url, commonResponse, params);
};
function commonResponse(data){
document.getElementById('bubblefishcontent').innerHTML=data.text;
document.getElementById('invite_button').innerHTML="";
gadgets.window.adjustHeight(1080);
};
function innerinvite(){
opensocial.requestShareApp("VIEWER_FRIENDS", null, function(response) {
if (response.hadError()) {
var errCode = response.getErrorCode();
} else {
var recipientIds = response.getData()["recipientIds"];
// alert(recipientIds);
if (recipientIds) {
var url="{{ game_server_url }}invite/iscallback/";
var params={};
var post_data = {snsId : snsId, ids : recipientIds };
params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(post_data);
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
gadgets.io.makeRequest(url, function(data){}, params);
}
}
});
};
function initRequest() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer");
req.send(function(data) {
if (!data.hadError()) {
var item = data.get("viewer");
if (!item.hadError()) {
var viewer = item.getData();
snsId = viewer.getId();
var nickname = viewer.getDisplayName();
var servletUrl="{{ game_server_url }}";//这是后台的入口,执行完init方法之后,就可以在后台取到当前登录的用户id了
var params={};
var post_data = {snsId : snsId};
params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(post_data);
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
gadgets.io.makeRequest(servletUrl,initResponse,params);
var title = nickname+"さんがバブル海に潜入しました";
var content = nickname+"さんが可愛いフィッシュ達と共に楽しみました!あなたも行って見ては?";
sendFeed(title, content);
}
}
});
};
function initResponse(resp){
var skey = resp.data.skey;
var swfUrl = "{{ media_url }}swf/v16/gameMain.swf?v=1.0";
gadgets.flash.embedFlash(swfUrl, "bubblefishcontent", Number("10"), {flashVars:"xn_sig_session_key="+skey+"&sns=yahooM", wmode:"window", allowScriptAccess:"always", height:"640px"});
};
gadgets.util.registerOnLoadHandler(initRequest);
gadgets.window.adjustHeight(850);
</script>
<div>
<button type="button" onclick="paymentPage();">payment</button>
</div>
<div id="invite_button">
<p style="text-align:right;"><a href="#" onclick="invitePage()"><img src="{{ media_url }}img/invite_yahoom/canvas_invite_btn.gif" /></a></p>
</div>
<div id="bubblefishcontent"></div>
]]>
</Content>
</Module>
三、后台处理代码
View Code
def generate_timestamp():
"""Get seconds since epoch (UTC)."""
return int(time.time())
def generate_nonce(length=8):
"""Generate pseudorandom number."""
return ''.join([str(random.randint(0, 9)) for i in range(length)])
def pay(request):
viewer_id = int(request.GET.get('opensocial_viewer_id','0'))
owner_id = int(request.GET.get('opensocial_owner_id', '1'))
if viewer_id != owner_id:
return HttpResponse('invalid')
member = get_member_by_sns_id(viewer_id)
#生成订单
if request.method == 'POST':
order_info = None
for rec in request.POST.keys():
rec = simplejson.loads(rec)
if rec.has_key('ITEMS'):
order_info = rec
if not order_info:
return HttpResponse('invalid-1')
items = order_info.get('ITEMS', [])
amount = order_info.get('AMOUNT', 0)
payment_id = order_info.get('PAYMENT_ID', '')
#购买的价格就是珍珠数量,购买的数量是固定值1.
price = items[0].get('PRICE', 0)
count = items[0].get('COUNT', 0)
if price * count != amount:
return HttpResponse('invalid-2')
#rmb = pearls_to_yen(count)
record = RechargeRecord(member_id=member.id,
payment_id = payment_id,
pearls = price,
rmb = amount,
status = 'UP')
record.save()
body = {'order_id': str(record.id), 'response_code': 'OK'}
body_string = simplejson.dumps(body, separators=(',',':'))
#充值成功给用户珍珠
elif request.method == 'GET':
order_id = int(request.GET.get('order_id', 0))
if order_id <= 0:
return HttpResponse('invalid-3')
record = RechargeRecord.objects.get(id=order_id, member_id=member.id)
result = finish_payment(record)
body = {'order_id': str(record.id), 'response_code': 'OK', 'amount': str(record.rmb)}
body_string = simplejson.dumps(body, separators=(',',':'))
hashed = hashlib.sha1(body_string)
params = {
"timestamp": generate_timestamp(),
"nonce": generate_nonce(),
"consumer_key" : consumer_key,
"body_hash": urllib.quote_plus(base64.b64encode(hashed.digest()).rstrip("=")),
}
base_string = '&'.join(['%s=%s' % (x, params[x]) for x in sorted(params.keys())])
hmac_str = hmac.new(consumer_secret, base_string, hashlib.sha1).digest()
sign = base64.b64encode(hmac_str).rstrip("=")
header_sign = "%s&signature=%s" % (base_string, urllib.quote(sign))
response = HttpResponse(body_string)
response['Content-type'] = 'application/json'
response['X-MBGA-PAYMENT-SIGNATURE'] = header_sign
return response
问题:生成的 signature总是不正确
原因:simplejson.dumps默认生成json数据有空格,需要处理一下
>>> a = {"one":1, "two":2}
>>> str_a = simplejson.dumps(a)
>>> str_a
'{"two": 2, "one": 1}'
>>> str_a2 = simplejson.dumps(a, separators=(',',':'))
>>> str_a2
'{"two":2,"one":1}'