Commit fca2f842 authored by Boxiang Sun's avatar Boxiang Sun Committed by Rafael Monnerat

slapos_subscription_request: Real Wechat Payment in purchase step

parent 042bd6bc
import random, string, hashlib, urllib2 import random, string, hashlib, urllib2, socket
from urlparse import urlparse
try: try:
import xml.etree.cElementTree as ET import xml.etree.cElementTree as ET
except ImportError: except ImportError:
...@@ -8,28 +9,12 @@ class WechatException(Exception): ...@@ -8,28 +9,12 @@ class WechatException(Exception):
def __init__(self, msg): def __init__(self, msg):
super(WechatException, self).__init__(msg) super(WechatException, self).__init__(msg)
# RapidSpace Wechat acocunt configuration
class Single(object):
_instance = None
PAYMENT_DONE = False
def __new__(cls, *args, **kw):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kw)
return cls._instance
def __init__(self):
pass
def finishThePayment(self):
self.APP_ID = "XXX"
APP_ID = "wxadebca31430703b0" # Wechat public account appid
MCH_ID = "" # Wechat merchant account ID
API_KEY = "" # Wechat merchant platform(pay.weixin.qq.com) -->账户设置 -->API安全 -->密钥设置
CREATE_IP = "" # The IP address which request the order to Wechat, aka: instance IP CREATE_IP = "" # The IP address which request the order to Wechat, aka: instance IP
# UFDODER_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder" # Wechat unified order API
UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # Wechat unified order API UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # Wechat unified order API
NOTIFY_URL = "your IP: port/Method" # Wechat payment callback method
QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery" QUERY_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery"
def generateRandomStr(random_length=24): def generateRandomStr(random_length=24):
...@@ -81,8 +66,9 @@ def convert_xml_to_dict(xml_content): ...@@ -81,8 +66,9 @@ def convert_xml_to_dict(xml_content):
return dict_content return dict_content
def convert_dict_to_xml(dict_content): def convert_dict_to_xml(self, dict_content):
dict_content['sign'] = calculateSign(dict_content, API_KEY) wechat_account_configuration = self.ERP5Site_getWechatPaymentConfiguration()
dict_content['sign'] = calculateSign(dict_content, wechat_account_configuration['API_KEY'])
xml = '' xml = ''
for key, value in dict_content.items(): for key, value in dict_content.items():
xml += '<{0}>{1}</{0}>'.format(key, value) xml += '<{0}>{1}</{0}>'.format(key, value)
...@@ -90,16 +76,48 @@ def convert_dict_to_xml(dict_content): ...@@ -90,16 +76,48 @@ def convert_dict_to_xml(dict_content):
return xml return xml
def getWechatQRCodeURL(self, order_id, price, amount): def getSandboxKey(self):
product_name = "Pre-order " + amount + " RapidSpace VM " SANDBOX_KEY_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"
return wechat_account_configuration = self.ERP5Site_getWechatPaymentConfiguration()
params = {}
params['mch_id'] = wechat_account_configuration['MCH_ID']
params['nonce_str'] = generateRandomStr()
params['sign'] = calculateSign(params, wechat_account_configuration['API_KEY'])
# construct XML str
request_xml_str = '<xml>'
for key, value in params.items():
if isinstance(value, basestring):
request_xml_str = '%s<%s><![CDATA[%s]]></%s>' % (request_xml_str, key, value, key, )
else:
request_xml_str = '%s<%s>%s</%s>' % (request_xml_str, key, value, key, )
request_xml_str = '%s</xml>' % request_xml_str
result = urllib2.Request(SANDBOX_KEY_URL, data=request_xml_str)
result_data = urllib2.urlopen(result)
result_read = result_data.read()
result_dict_content = convert_xml_to_dict(result_read)
return_code = result_dict_content.get('return_code', '')
if return_code=="SUCCESS":
result_msg = result_dict_content['return_msg']
if result_msg=="ok":
sandbox_signkey = result_dict_content['sandbox_signkey']
return sandbox_signkey
raise Exception(result_dict_content['result_msg'].encode('utf-8'))
raise Exception("Get sanbox key failed: " + str(result_dict_content))
# TODO: waiting for the APP_ID def getWechatQRCodeURL(self, order_id, price, amount):
appid = APP_ID # XXXXXXXXXXXXXXXXXXXXXXXXXXxx portal = self.getPortalObject()
mch_id = MCH_ID base_url = portal.absolute_url()
key = API_KEY NOTIFY_URL = base_url + "/Base_receiveWechatPaymentNotify" # Wechat payment callback method
wechat_account_configuration = self.ERP5Site_getWechatPaymentConfiguration()
appid = wechat_account_configuration['APP_ID']
mch_id = wechat_account_configuration['MCH_ID']
key = wechat_account_configuration['API_KEY']
# This is for sandbox test
# key = getSandboxKey() # API_KEY
nonce_str = generateRandomStr() nonce_str = generateRandomStr()
spbill_create_ip = CREATE_IP
result = urlparse(base_url)
spbill_create_ip = socket.gethostbyname(result.netloc)
notify_url = NOTIFY_URL notify_url = NOTIFY_URL
trade_type = "NATIVE" trade_type = "NATIVE"
...@@ -109,14 +127,16 @@ def getWechatQRCodeURL(self, order_id, price, amount): ...@@ -109,14 +127,16 @@ def getWechatQRCodeURL(self, order_id, price, amount):
params['mch_id'] = mch_id params['mch_id'] = mch_id
params['nonce_str'] = nonce_str params['nonce_str'] = nonce_str
params['out_trade_no'] = order_id.encode('utf-8') params['out_trade_no'] = order_id.encode('utf-8')
params['total_fee'] = amount * 100 # unit is Fen, 1 CHY = 100 Fen # This is for sandbox test, sandbox need the total_fee equal to 101 exactly
# params['total_fee'] = 101 # int(-(price * 100)) # unit is Fen, 1 CHY = 100 Fen
params['total_fee'] = int(-(price * 100)) # unit is Fen, 1 CHY = 100 Fen
params['spbill_create_ip'] = spbill_create_ip params['spbill_create_ip'] = spbill_create_ip
params['notify_url'] = notify_url params['notify_url'] = notify_url
params['body'] = product_name.encode('utf-8') params['body'] = "Rapid Space VM machine".encode('utf-8')
params['trade_type'] = trade_type params['trade_type'] = trade_type
# generate signature # generate signature
params['sign'] = calculateSign(params, API_KEY) params['sign'] = calculateSign(params, key)
# construct XML str # construct XML str
request_xml_str = '<xml>' request_xml_str = '<xml>'
...@@ -137,11 +157,11 @@ def getWechatQRCodeURL(self, order_id, price, amount): ...@@ -137,11 +157,11 @@ def getWechatQRCodeURL(self, order_id, price, amount):
result_code = result_dict_content['result_code'] result_code = result_dict_content['result_code']
if result_code=="SUCCESS": if result_code=="SUCCESS":
code_url = result_dict_content['code_url'] code_url = result_dict_content['code_url']
return "weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00" return code_url
else: else:
print("Error description: {0}".format(result_dict_content.get("err_code_des"))) raise Exception("Error description: {0}".format(result_dict_content.get("err_code_des")))
else: else:
print("Error description: {0}".format(result_dict_content.get("return_msg"))) raise Exception("Error description: {0}".format(result_dict_content.get("return_msg")))
def receiveWechatPaymentNotify(self, request, *args, **kwargs): def receiveWechatPaymentNotify(self, request, *args, **kwargs):
...@@ -167,11 +187,18 @@ def receiveWechatPaymentNotify(self, request, *args, **kwargs): ...@@ -167,11 +187,18 @@ def receiveWechatPaymentNotify(self, request, *args, **kwargs):
<transaction_id><![CDATA[4200000031201712112434025551875]]></transaction_id> <transaction_id><![CDATA[4200000031201712112434025551875]]></transaction_id>
</xml> </xml>
''' '''
return '''
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
'''
wechat_account_configuration = self.ERP5Site_getWechatPaymentConfiguration()
params = convert_xml_to_dict(request.body) params = convert_xml_to_dict(request.body)
if params.get("return_code") == "SUCCESS": if params.get("return_code") == "SUCCESS":
# Connection is ok # Connection is ok
sign = params.pop('sign') sign = params.pop('sign')
recalcualted_sign = calculateSign(params, API_KEY) recalcualted_sign = calculateSign(params, wechat_account_configuration['API_KEY'])
if recalcualted_sign == sign: if recalcualted_sign == sign:
if params.get("result_code", None) == "SUCCESS": # payment is ok if params.get("result_code", None) == "SUCCESS": # payment is ok
pass pass
...@@ -202,20 +229,20 @@ def queryWechatOrderStatus(self, dict_content): ...@@ -202,20 +229,20 @@ def queryWechatOrderStatus(self, dict_content):
- transaction_id (str): wechat order number, use this in higher priority, it will return in the payment notify callback - transaction_id (str): wechat order number, use this in higher priority, it will return in the payment notify callback
- out_trade_no(str): The order ID used inside ERP5, less than 32 characters, digits, alphabets, and "_-|*@", unique in ERP5 - out_trade_no(str): The order ID used inside ERP5, less than 32 characters, digits, alphabets, and "_-|*@", unique in ERP5
''' '''
if APP_ID == "XXX": return "XXXS"
return "SUCCESS"
if "transaction_id" not in dict_content and "out_trade_no" not in dict_content: if "transaction_id" not in dict_content and "out_trade_no" not in dict_content:
raise WechatException("transaction_id or out_trade_no is needed for query the Wechat Order") raise WechatException("transaction_id or out_trade_no is needed for query the Wechat Order")
wechat_account_configuration = self.ERP5Site_getWechatPaymentConfiguration()
params = { params = {
"appid": APP_ID, "appid": wechat_account_configuration['APP_ID'],
"mch_id": MCH_ID, "mch_id": wechat_account_configuration['MCH_ID'],
"nonce_str": generateRandomStr(), "nonce_str": generateRandomStr(),
"transaction_id": dict_content.get("transaction_id", ""), "transaction_id": dict_content.get("transaction_id", ""),
"out_trade_no": dict_content.get("out_trade_no", ""), "out_trade_no": dict_content.get("out_trade_no", ""),
} }
sign = calculateSign(params, API_KEY) sign = calculateSign(params, wechat_account_configuration['API_KEY'])
params["sign"] = sign params["sign"] = sign
# xml_str = convert_dict_to_xml(params) # xml_str = convert_dict_to_xml(params)
return None return None
......
...@@ -46,9 +46,8 @@ ...@@ -46,9 +46,8 @@
<key> <string>text_content_warning_message</string> </key> <key> <string>text_content_warning_message</string> </key>
<value> <value>
<tuple> <tuple>
<string>W: 27, 0: Cannot decode using encoding "ascii", unexpected byte at position 63 (invalid-encoded-data)</string> <string>W:196, 2: Unreachable code (unreachable)</string>
<string>W: 98, 2: Unreachable code (unreachable)</string> <string>W:233, 2: Unreachable code (unreachable)</string>
<string>W:139, 6: Unused variable \'code_url\' (unused-variable)</string>
</tuple> </tuple>
</value> </value>
</item> </item>
......
# inspired by Pack_generateCode128BarcodeImage in sanef-evl project # inspired by Pack_generateCode128BarcodeImage in sanef-evl project
code_url = code_url + "&trade_no=" + trade_no
return context.Base_generateBarcodeImage('qrcode', code_url) return context.Base_generateBarcodeImage('qrcode', code_url)
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>code_url, trade_no</string> </value> <value> <string>code_url</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_getWechatPaymentConfiguration</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -43,9 +43,17 @@ payment = person.Person_restrictMethodAsShadowUser( ...@@ -43,9 +43,17 @@ payment = person.Person_restrictMethodAsShadowUser(
if batch_mode: if batch_mode:
return {'subscription' : subscription_request.getRelativeUrl(), 'payment': payment.getRelativeUrl() } return {'subscription' : subscription_request.getRelativeUrl(), 'payment': payment.getRelativeUrl() }
def wrapGetPriceWithShadow(payment):
return payment.PaymentTransaction_getTotalPayablePrice()
price = person.Person_restrictMethodAsShadowUser(
shadow_document=person,
callable_object=wrapGetPriceWithShadow,
argument_list=[payment,])
if payment_mode == "wechat": if payment_mode == "wechat":
portal = context.getPortalObject() portal = context.getPortalObject()
code_url = portal.Base_getWechatCodeURL(subscription_request.getId(), payment.PaymentTransaction_getTotalPayablePrice(), user_input_dict["amount"]) code_url = portal.Base_getWechatCodeURL(subscription_request.getId(), price, user_input_dict["amount"])
web_site = context.getWebSiteValue() web_site = context.getWebSiteValue()
base_url = web_site.absolute_url() base_url = web_site.absolute_url()
return context.REQUEST.RESPONSE.redirect( return context.REQUEST.RESPONSE.redirect(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment