Commit 042bd6bc authored by Boxiang Sun's avatar Boxiang Sun Committed by Rafael Monnerat

slapos_subscription_request: Add Proof-of-concept Wechat payment support

parent 7659b305
import random, string, hashlib, urllib2
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
class WechatException(Exception):
def __init__(self, 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
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"
def generateRandomStr(random_length=24):
alpha_num = string.ascii_letters + string.digits
random_str = ''.join(random.choice(alpha_num) for i in range(random_length))
return random_str
def calculateSign(dict_content, key):
# Calculate the sign according to the data_dict
# The rule was defined by Wechat (Wrote in Chinese):
# https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
# 1. Sort it by dict order
params_list = sorted(dict_content.items(), key=lambda e: e[0], reverse=False)
# 2. Concatenate the list to a string
params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list)
# 3. Add trade key in the end
params_str = params_str + '&key=' + key
md5 = hashlib.md5() # Use MD5 mode
md5.update(params_str.encode('utf-8'))
sign = md5.hexdigest().upper()
return sign
def convert_xml_to_dict(xml_content):
'''
The XML returned by Wechat is like:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
<mch_id><![CDATA[10000100]]></mch_id>
<nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
<openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>
'''
try:
t = ET.XML(xml_content)
except ET.ParseError:
return {}
else:
dict_content = dict([(child.tag, child.text) for child in t])
return dict_content
def convert_dict_to_xml(dict_content):
dict_content['sign'] = calculateSign(dict_content, API_KEY)
xml = ''
for key, value in dict_content.items():
xml += '<{0}>{1}</{0}>'.format(key, value)
xml = '<xml>{0}</xml>'.format(xml)
return xml
def getWechatQRCodeURL(self, order_id, price, amount):
product_name = "Pre-order " + amount + " RapidSpace VM "
return
# TODO: waiting for the APP_ID
appid = APP_ID # XXXXXXXXXXXXXXXXXXXXXXXXXXxx
mch_id = MCH_ID
key = API_KEY
nonce_str = generateRandomStr()
spbill_create_ip = CREATE_IP
notify_url = NOTIFY_URL
trade_type = "NATIVE"
# Construct parameter for calling the Wechat payment URL
params = {}
params['appid'] = appid
params['mch_id'] = mch_id
params['nonce_str'] = nonce_str
params['out_trade_no'] = order_id.encode('utf-8')
params['total_fee'] = amount * 100 # unit is Fen, 1 CHY = 100 Fen
params['spbill_create_ip'] = spbill_create_ip
params['notify_url'] = notify_url
params['body'] = product_name.encode('utf-8')
params['trade_type'] = trade_type
# generate signature
params['sign'] = calculateSign(params, 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
# send data
result = urllib2.Request(UFDODER_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['return_code']
if return_code=="SUCCESS":
result_code = result_dict_content['result_code']
if result_code=="SUCCESS":
code_url = result_dict_content['code_url']
return "weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00"
else:
print("Error description: {0}".format(result_dict_content.get("err_code_des")))
else:
print("Error description: {0}".format(result_dict_content.get("return_msg")))
def receiveWechatPaymentNotify(self, request, *args, **kwargs):
'''
Receive the asychonized callback send by Wechat after user pay the order.
Wechat will give us something like:
<xml>
<appid><![CDATA[wx6509f6e240dfae50]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[14323929292]]></mch_id>
<nonce_str><![CDATA[aCJv0SAwKY5Cxfi34mtCEM5SdNKexuXgnW]]></nonce_str>
<openid><![CDATA[oHWl5w5M34hYM-ox2mn6Xatse7yCTs]]></openid>
<out_trade_no><![CDATA[aHQDJyacUSGC]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[C4F8B5B17A3E6203491A3B790A1D87ECEA]]></sign>
<time_end><![CDATA[201712114144230]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[NATIVE]]></trade_type>
<transaction_id><![CDATA[4200000031201712112434025551875]]></transaction_id>
</xml>
'''
params = convert_xml_to_dict(request.body)
if params.get("return_code") == "SUCCESS":
# Connection is ok
sign = params.pop('sign')
recalcualted_sign = calculateSign(params, API_KEY)
if recalcualted_sign == sign:
if params.get("result_code", None) == "SUCCESS": # payment is ok
pass
# order number
# out_trade_no = params.get("out_trade_no")
# Wechat payment order ID
# This is what we should use when we search the order in the wechat
# transaction_id = params.get("out_trade_no")
# Save the wechat payment order ID in somewhere.
# We recevied the payment...
# Process something
# XXX: display the page the payment received.
# container.REQUEST.RESPONSE.redirect("%s/#wechat_payment_confirmed")
# We must tell Wechat we received the response. Otherwise wechat will keep send it within 24 hours
# xml_str = convert_dict_to_xml({"return_code": "SUCCESS"})
# return container.REQUEST.RESPONSE(xml_str)
else:
print("{0}:{1}".format(params.get("err_code"), params.get("err_code_des")))
else:
# Error information
print(params.get("return_msg").encode("utf-8"))
def queryWechatOrderStatus(self, dict_content):
'''
query url: https://api.mch.weixin.qq.com/pay/orderquery
documentation(Chinese): https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
The dict_content atleast should contains one of following:
- 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
'''
if APP_ID == "XXX":
return "SUCCESS"
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")
params = {
"appid": APP_ID,
"mch_id": MCH_ID,
"nonce_str": generateRandomStr(),
"transaction_id": dict_content.get("transaction_id", ""),
"out_trade_no": dict_content.get("out_trade_no", ""),
}
sign = calculateSign(params, API_KEY)
params["sign"] = sign
# xml_str = convert_dict_to_xml(params)
return None
# return "SUCCESS"
# result = urllib2.Request(QUERY_URL, data=xml_str)
# result_data = urllib2.urlopen(result)
# result_read = result_data.read()
#result_dict_content = convert_xml_to_dict(result_read)
# TBC
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Extension Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>WechatUtils</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.WechatUtils</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Extension Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple>
<string>W: 27, 0: Cannot decode using encoding "ascii", unexpected byte at position 63 (invalid-encoded-data)</string>
<string>W: 98, 2: Unreachable code (unreachable)</string>
<string>W:139, 6: Unused variable \'code_url\' (unused-variable)</string>
</tuple>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
<none/>
</tuple>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Payment Transaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>source_payment/organisation_module/slapos/bank_account</string>
<string>source_section/organisation_module/slapos</string>
<string>price_currency/currency_module/EUR</string>
<string>specialise/sale_trade_condition_module/slapos_aggregated_trade_condition</string>
<string>resource/currency_module/CNY</string>
<string>payment_mode/wechat</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_destination_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_wechat_pre_payment_template</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Payment Transaction</string> </value>
</item>
<item>
<key> <string>start_date</string> </key>
<value>
<object>
<klass>
<global id="1.1" name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1533254400.0</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>stop_date</string> </key>
<value>
<object>
<klass> <reference id="1.1"/> </klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1533313457.88</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Reservation</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Accounting Transaction Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>destination/account_module/payable</string>
<string>source/account_module/receivable</string>
</tuple>
</value>
</item>
<item>
<key> <string>grouping_date</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>grouping_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>1</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Accounting Transaction Line</string> </value>
</item>
<item>
<key> <string>price</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>quantity</string> </key>
<value> <float>0.0</float> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Accounting Transaction Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>destination/account_module/bank</string>
<string>source/account_module/bank</string>
</tuple>
</value>
</item>
<item>
<key> <string>grouping_date</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>grouping_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>2</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Accounting Transaction Line</string> </value>
</item>
<item>
<key> <string>price</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>quantity</string> </key>
<value> <float>0.0</float> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Sale Invoice Transaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>source_section/organisation_module/slapos</string>
<string>payment_mode/payzen</string>
<string>specialise/sale_trade_condition_module/slapos_aggregated_trade_condition</string>
<string>price_currency/currency_module/CNY</string>
<string>resource/currency_module/CNY</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_destination_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>template_wechat_pre_payment_subscription_sale_invoice_transaction</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Sale Invoice Transaction</string> </value>
</item>
<item>
<key> <string>start_date</string> </key>
<value>
<object>
<klass>
<global id="1.1" name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1325376000.0</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>stop_date</string> </key>
<value>
<object>
<klass> <reference id="1.1"/> </klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1326582000.0</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>template_wechat_pre_payment_subscription_sale_invoice_transaction</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Invoice Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>use/trade/sale</string>
<string>resource/service_module/slapos_reservation_fee</string>
<string>quantity_unit/unit/piece</string>
<string>base_contribution/base_amount/invoicing</string>
</tuple>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>1</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>1</string> </value>
</item>
<item>
<key> <string>index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>int_index</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Invoice Line</string> </value>
</item>
<item>
<key> <string>price</string> </key>
<value> <float>188.0</float> </value>
</item>
<item>
<key> <string>quantity</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>stop_date</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1326585600.0</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>movement</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
web_site = context.getWebSiteValue()
base_url = web_site.absolute_url()
# portal = context.getPortalObject()
# subscription_request = portal.subscription_request_module.get(trade_no)
context.Base_finishThePayment()
# if not subscription_request:
# raise Exception("Order not found")
# subscription_request.confirmed()
return context.REQUEST.RESPONSE.redirect("%s/gadget_rapid_page_order_wechat_simulation_confirm.html/" % base_url)
<?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>trade_no</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Anonymous</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_WechatPaymentSimulation</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>finishThePayment</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>WechatUtils</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_finishThePayment</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# inspired by Pack_generateCode128BarcodeImage in sanef-evl project
code_url = code_url + "&trade_no=" + trade_no
return context.Base_generateBarcodeImage('qrcode', code_url)
<?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>code_url, trade_no</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_generateWechatQRCodeFromCodeURL</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>getWechatQRCodeURL</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>WechatUtils</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getWechatCodeURL</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>queryWechatOrderStatus</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>WechatUtils</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_queryWechatOrderStatus</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
if not trade_no:
raise Exception("Unknown trade number")
return context.Base_queryWechatOrderStatus({'out_trade_no': trade_no})
<?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>trade_no=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_queryWechatOrderStatusByTradeNo</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>receiveWechatPaymentNotify</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>WechatUtils</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_receiveWechatPaymentNotify</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# Example code:
# Import a standard function, and get the HTML request and response objects.
from Products.PythonScripts.standard import html_quote
request = container.REQUEST
response = request.response
# Return a string identifying this script.
print "This is the", script.meta_type, '"%s"' % script.getId(),
if script.title:
print "(%s)" % html_quote(script.title),
print "in", container.absolute_url()
return printed
<?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>id</string> </key>
<value> <string>Base_redirectWechatPaymentSuccessPage</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -19,6 +19,6 @@ user_input_dict = {
"amount" : amount}
return context.SubscriptionRequestModule_requestSubscriptionProxy(
default_email_text, subscription_reference,
default_email_text, subscription_reference, payment_mode,
confirmation_required=bool(confirmation_required),
user_input_dict=user_input_dict, batch_mode=0)
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>name=None, default_email_text=None, subscription_reference=None, amount=0, confirmation_required=True, REQUEST=None, **kw</string> </value>
<value> <string>name=None, default_email_text=None, payment_mode=None, subscription_reference=None, amount=0, confirmation_required=True, REQUEST=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -7,15 +7,16 @@ if REQUEST is not None:
person, person_is_new = context.SubscriptionRequest_createUser(email, user_input_dict['name'])
web_site = context.getWebSiteValue()
# Check if user is already exist, otherwise redirect to ask confirmation
if confirmation_required and not person_is_new:
# if payment mode not selected, ask user to confirm and select one.
if not payment_mode:
base_url = web_site.absolute_url()
return context.REQUEST.RESPONSE.redirect(
"%s/#order_confirmation?name=%s&email=%s&amount=%s&subscription_reference=%s" % (
base_url,
person.getTitle(),
person.getDefaultEmailText(),
email,
user_input_dict["amount"],
subscription_reference))
......@@ -27,21 +28,29 @@ subscription_request = context.subscription_request_module.newContent(
subscription_request.setDefaultEmailText(email)
def wrapWithShadow(subscription_request, amount, subscription_reference):
def wrapWithShadow(subscription_request, amount, subscription_reference, payment_mode):
subscription_request.activate(tag="subscription_condition_%s" % subscription_request.getId()
).SubscriptionRequest_applyCondition(subscription_reference)
return subscription_request.SubscriptionRequest_requestPaymentTransaction(amount=amount,
tag="subscription_%s" % subscription_request.getId())
tag="subscription_%s" % subscription_request.getId(), payment_mode=payment_mode)
payment = person.Person_restrictMethodAsShadowUser(
shadow_document=person,
callable_object=wrapWithShadow,
argument_list=[subscription_request, user_input_dict["amount"], subscription_reference])
argument_list=[subscription_request, user_input_dict["amount"], subscription_reference, payment_mode])
if batch_mode:
return {'subscription' : subscription_request.getRelativeUrl(), 'payment': payment.getRelativeUrl() }
if payment_mode == "wechat":
portal = context.getPortalObject()
code_url = portal.Base_getWechatCodeURL(subscription_request.getId(), payment.PaymentTransaction_getTotalPayablePrice(), user_input_dict["amount"])
web_site = context.getWebSiteValue()
base_url = web_site.absolute_url()
return context.REQUEST.RESPONSE.redirect(
"%s/#wechat_payment?amount=%s&trade_no=%s&code_url=%s" % (base_url, user_input_dict["amount"], subscription_request.getId(), code_url))
def wrapRedirectWithShadow(payment_transaction, web_site):
return payment_transaction.PaymentTransaction_redirectToManualPayzenPayment(web_site)
......
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>email, subscription_reference, confirmation_required=False, user_input_dict={}, batch_mode=True, REQUEST=None</string> </value>
<value> <string>email, subscription_reference, payment_mode, confirmation_required=False, user_input_dict={}, batch_mode=True, REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
......@@ -7,7 +7,10 @@ portal = context.getPortalObject()
current_invoice = context.getCausalityValue()
if current_invoice is None:
invoice_template_path = "accounting_module/template_pre_payment_subscription_sale_invoice_transaction"
if payment_mode == "wechat":
invoice_template_path = "accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction"
else:
invoice_template_path = "accounting_module/template_pre_payment_subscription_sale_invoice_transaction"
invoice_template = portal.restrictedTraverse(invoice_template_path)
current_invoice = invoice_template.Base_createCloneDocument(batch_mode=1)
......
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>amount, tag, payment, REQUEST=None</string> </value>
<value> <string>amount, tag, payment, payment_mode, REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
......@@ -8,8 +8,13 @@ current_invoice = context.getCausalityValue()
current_payment = None
if current_invoice is None:
payment_template = portal.restrictedTraverse("accounting_module/slapos_pre_payment_template")
# Hardcoded value for reservation
if payment_mode == "wechat":
payment_template = portal.restrictedTraverse("accounting_module/slapos_wechat_pre_payment_template")
quantity = int(amount) * 188
else:
payment_template = portal.restrictedTraverse("accounting_module/slapos_pre_payment_template")
quantity = int(amount)*25
current_payment = payment_template.Base_createCloneDocument(batch_mode=1)
current_payment.edit(
......@@ -22,8 +27,7 @@ if current_invoice is None:
stop_date=DateTime()
)
# Hardcoded value for reservation
quantity = int(amount)*25
for line in current_payment.contentValues():
if line.getSource() == "account_module/bank":
line.setQuantity(-1*quantity)
......@@ -39,6 +43,6 @@ if current_invoice is None:
context.reindexObject(activate_kw={'tag': tag})
context.activate(tag=tag).SubscriptionRequest_createRelatedSaleInvoiceTransaction(
amount, tag, current_payment.getRelativeUrl())
amount, tag, current_payment.getRelativeUrl(), payment_mode)
return current_payment
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>amount, tag, REQUEST=None</string> </value>
<value> <string>amount, tag, payment_mode, REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
extension.erp5.SlapOSSubscriptionRequest
\ No newline at end of file
extension.erp5.SlapOSSubscriptionRequest
extension.erp5.WechatUtils
\ No newline at end of file
accounting_module/slapos_pre_payment_template
accounting_module/slapos_pre_payment_template/**
accounting_module/slapos_wechat_pre_payment_template
accounting_module/slapos_wechat_pre_payment_template/**
accounting_module/template_pre_payment_subscription_sale_invoice_transaction
accounting_module/template_pre_payment_subscription_sale_invoice_transaction/**
accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction
accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction/**
notification_message_module/slapos-free-trial-token
portal_alarms/slapos_subscription_request_process_**
portal_alarms/slapos_trial_process_**
\ No newline at end of file
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