天天看點

openerp支付寶當面支付(掃條碼)

 openerp要內建支付寶支付,我們項目一般都面對例如咖啡廳、農貿市場、超市等等收銀台使用的螢幕上面,一般使用的螢幕都是安卓系統。這裡我們隻考慮我們的erp的建立,當然一個這樣的項目包括很多,我們這裡隻是涉及到支付寶支付方面,并且由于隻是使用到掃描槍的情況,符合這種場景的支付方式就是支付寶的face2face當面付的方式。

        支付的當面付在開始之前,必須要以商家的方式申請在支付寶的開放平台的的當面付的簽證,具體檢視支付寶的開放平台的介紹(誰叫你使用人家的東西,一切按照官網的内容為準)。這個時候你就會收到支付寶的當面付的appid,支付寶的私鑰、公鑰,這些都是要用上在開發過程中,這裡不得不吐槽一下支付寶,比起微信支付,傳輸的參數要多很多,并且安全級别要高,當面付現在隻是支援RSA簽名編碼跟解碼,後面會說到底怎麼個麻煩法。

   在開始之前我們要先準備一下相關内容跟參數:

   1.商家的appid,具體怎麼檢視,查詢支付寶官網,不行就找客服(支付寶的客服還是不錯的)

   2.用openssel生産自己(開發者的)私鑰以及公鑰,具體還是看官網,裡面有詳細的介紹

   3.上傳你生産的公鑰到支付寶用于解碼我們生産的sign簽名,并且複制支付寶的公鑰到本地用于解碼支付寶傳回來的sign簽名

   開發流程:

    1.建立一個openerp子產品:payment_alipay(具體不同版本可能有些不相同,我用的openerp7)

openerp支付寶當面支付(掃條碼)
   2.主要處理代碼:
openerp支付寶當面支付(掃條碼)

   3.開發本質上面就是開發一個RESTful過程,就是通路url+參數,我程式接收到post過來的資料然後進行處理

   相關代碼内容:

mail.py(主要是做route過程)

@http.route('/payment/alipay/scancode', type='json', auth='none', methods=['POST'])
def alipay_scancode(self, **post):
    logger.info('Beginning Alipay notify with post data %s', pprint.pformat(post))  # debug
return_pay_msg = {'return_code': 'FAIL', 'return_msg': ''}
    cr, uid, context = request.cr, SUPERUSER_ID, request.context
try:
        money = float(post['money'])
except ValueError:
        return_pay_msg['return_msg'] = u'傳輸的金錢格式有問,隻能為數字!'
return return_pay_msg

if post['orderID'] and isinstance(money, float) and post['auth_code'] and post['subject']:
        pay_record = request.registry['payment.record']  # 注冊addons裡面的payment_record.models.payment_record.py
        # 裡面的class PaymentRecord
record = dict()
        record['ref_id'] = str(post['orderID'])
        record['type'] = u'支付寶'
record['sub_type'] = u'支付寶掃條形碼支付'
record['fee'] = str(post['money'])
        record['subject'] = post['subject']
        record_id = pay_record.create_alipay_record(cr, uid, record)  # return record['record_id']

post_data = dict()
        post_data['out_trade_no'] = record_id  # out_trade_no= reference of Payment Transaction
post_data['money'] = str(post['money'])
        post_data['auth_code'] = post['auth_code']
        post_data['subject'] = post['subject']

        res = request.registry['payment.acquirer'].make_data_and_post(cr, uid, post_data, context)  # 處理資料并post到阿裡
if res:
            res_db = request.registry['payment.transaction'].pay_record_to_db(cr, uid, res, context)
if res_db:
                return_pay_msg['return_code'] = 'SUCCESS'
return_pay_msg['return_msg'] = u'支付成功并寫入資料庫'
return return_pay_msg
else:
            return_pay_msg['return_msg'] = u'支付處理出現問題!查詢資料處理子產品狀态.'
return return_pay_msg
else:
        return_pay_msg['return_msg'] = u'請檢查post的參數,一定要包括orderID、money、auth_code、subject!'
print return_pay_msg['return_msg']
print return_pay_msg
return return_pay_msg      

alipay.py(跟支付寶互動的處理)

def encode_dict(self, params):  # 處理編碼問題,都是設定成utf-8
return {k: six.u(v).encode('utf-8')
if isinstance(v, str) else v.encode('utf-8')
if isinstance(v, six.string_types) else v
for k, v in six.iteritems(params)}

def make_data_and_post(self, cr, uid, post_data, context=None):
    ids = self.search(cr, uid, [('provider', '=', 'alipay')], context=context)
    tz = timezone(country_timezones('cn')[0])  # 擷取目前時區
now_cst = datetime.datetime.now(tz)  # 根據目前時區生産時間
biz_content = "{\"out_trade_no\":\""+post_data['out_trade_no']+"\","
biz_content += "\"scene\":\"bar_code\","
biz_content += "\"auth_code\":\""+post_data['auth_code']+"\","
biz_content += "\"total_amount\":\"" + post_data['money'] +"\",\"discountable_amount\":\"0.00\","
biz_content += "\"subject\":\""+post_data['subject']+"\",\"body\":\"test\","
biz_content += "\"goods_detail\":[{\"goods_id\":\"apple-01\",\"goods_name\":\"ipad\",\"goods_category\":\"7788230\",\"price\":\"88.00\",\"quantity\":\"1\"},{\"goods_id\":\"apple-02\",\"goods_name\":\"iphone\",\"goods_category\":\"7788231\",\"price\":\"88.00\",\"quantity\":\"1\"}],"
biz_content += "\"operator_id\":\"op001\",\"store_id\":\"pudong001\",\"terminal_id\":\"t_001\","
biz_content += "\"timeout_express\":\"5m\"}"
del post_data['out_trade_no']  # 公共請求參數不需要
del post_data['auth_code']  # 公共請求參數不需要
del post_data['money']  # 公共請求參數不需要
del post_data['subject']
if ids:
        acquirer = self.browse(cr, uid, ids[0], context=None)
        post_data.update(app_id='2015052600089077')
        post_data.update(charset='UTF-8')
        post_data.update(method='alipay.trade.pay')
        post_data.update(timestamp=now_cst.strftime("%Y-%m-%d %H:%M:%S"))
        post_data.update(version='1.0')
        post_data.update(sign_type='RSA')
        post_data.update(biz_content=biz_content)
        cpd = util.params_filter(post_data)
if cpd:
            sign = util.build_mysign(cpd, sign_type='RSA')
            post_data.update(sign=sign)
try:
                back_info = self.post_alipay(cr, uid, post_data, acquirer.environment, context=None)
return back_info
except Exception, e:
return e
else:
return u'組合字元串有問題!'
else:
return u'沒有建立支付寶支付模式'

def post_alipay(self, cr, uid, post_data, environment, context=None):
    alipay_url = self.get_alipay_urls(cr, uid, environment, context=None)
    pay_state_info = self.post_to_alipay_url(alipay_url, post_data)
return pay_state_info

def post_to_alipay_url(self, url, data):
    headers = {'Content-type': 'application/json;charset=utf-8'}
    data = self.encode_dict(data)
# url_values = urllib.urlencode(self.encode_dict(data))
    # url_values = util.params_filter1(data)
    # print "==========url_values=========="
    # print url_values
    # res = Request(url, url_values, headers)
    # try:
    #     rsp = urlopen(res)
    #     redirect_url = rsp.geturl()
    #     print '===========redirectUrl========='
    #     print redirect_url
    #     the_back_info = rsp.read()
    #     print '===========the_back_info========'
    #     print the_back_info
    #     rsp.close()
    #     return the_back_info
    # except URLError, e:
    #     if hasattr(e, 'reason'):
    #         print u'不能通路到達改伺服器'
    #         print u'原因是:', e.reason
    #         return e.reason
    # except HTTPError, e:
    #     print u'錯誤代碼:', e.code
    #     return e.code
data = urllib.urlencode(data)
# rsp = requests.post(url, data=json.dumps(data), headers=headers)
rsp = requests.get(url, params=data, headers=headers)
if rsp.status_code != 200:
return False
if rsp.text:
return rsp.text      

util.py(生産傳輸參數,以及sign的生産)

def params_filter(params):
    ks = params.keys()
    ks.sort()
    newparams = {}
    prestr = ''
for k in ks:
        v = params[k]
# k = smart_str(k, 'utf-8')
if k not in 'sign' and v != '':
            newparams[k] = v
# newparams[k] = smart_str(v, 'utf-8')
prestr += '%s=%s&' % (k, newparams[k])
    prestr = prestr[:-1]
return prestr


def build_mysign(prestr, sign_type='MD5'):
if sign_type == 'MD5':  # 這裡的md5估計用不了,後面有需求再修改
return md5_constructor(prestr).hexdigest()
elif sign_type == 'RSA':
# 打開私匙檔案并轉換成python rsa的private key格式
with open(r'D:\alipaykey\rsa_private_key.pem') as priv_key:
            privkey = priv_key.read()
        privatekey = rsa.PrivateKey.load_pkcs1(privkey)
        signature = rsa.sign(prestr, privatekey, 'SHA-1')
        sign = base64.b64encode(signature)
return sign      

不得不說這裡的難點,就是python的rsa,在生産支付寶的sign過程中嘗試了很多,首先了解一下python的rsa,附上rsa官網:https://stuvel.eu/python-rsa-doc/index.html

一般情況就是:

>>> (pubkey,privkey)=rsa.newkeys(512)   # 使用python rsa生産的公鑰和私鑰>>> message='Go left at the blue tree'>>> signature=rsa.sign(message,privkey,'SHA-1')   # 根據私鑰生成的SHA-1的簽名      

解碼sign:

>>> message='Go left at the blue tree'>>> rsa.verify(message,signature,pubkey)True      

但是現在我們的情況是我們已經用openssl生産了自己的私鑰和公鑰,這也是支付寶要求用openssl生産的,我們還是用openssl生産吧,但是問題來了,python的rsa跟openssl的私鑰、公鑰的格式不一樣的,大家可以嘗試列印一下rsa生産的key,它是一大串的數字串,但是openssl生産的是有數字、符号、字母等等的字元串!我在這裡卡了很久,後面還是在同僚幫忙下找到了解決方法:

rsa.PrivateKey.load_pkcs1()

 以及

rsa.PublicKey.load_pkcs1()

 方法,專門是用來跟openssl生産的key互動的,其實rsa是可以生産openssl格式的key,這裡具體不去做嘗試了,具體詳細看官網!

with open(r'D:\alipaykey\rsa_private_key.pem') as priv_key:
            privkey = priv_key.read()
        privatekey = rsa.PrivateKey.load_pkcs1(privkey)
        signature = rsa.sign(prestr, privatekey, 'SHA-1')      

這樣生産signture是符合支付寶的sign簽名要求的,以上還是一個開始,因為後面有