yahooM充值接入

Posted on 2011-04-18 11:28  蛇小狼  阅读(745)  评论(0编辑  收藏  举报

一、接入流程
①    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}'