openerp要集成支付宝支付,我们项目一般都面对例如咖啡厅、农贸市场、超市等等收银台使用的屏幕上面,一般使用的屏幕都是安卓系统。这里我们只考虑我们的erp的建立,当然一个这样的项目包括很多,我们这里只是涉及到支付宝支付方面,并且由于只是使用到扫描枪的情况,符合这种场景的支付方式就是支付宝的face2face当面付的方式。

        支付的当面付在开始之前,必须要以商家的方式申请在支付宝的开放平台的的当面付的签证,具体查看支付宝的开放平台的介绍(谁叫你使用人家的东西,一切按照官网的内容为准)。这个时候你就会收到支付宝的当面付的appid,支付宝的私钥、公钥,这些都是要用上在开发过程中,这里不得不吐槽一下支付宝,比起微信支付,传输的参数要多很多,并且安全级别要高,当面付现在只是支持RSA签名编码跟解码,后面会说到底怎么个麻烦法。

   在开始之前我们要先准备一下相关内容跟参数:

   1.商家的appid,具体怎么查看,查询支付宝官网,不行就找客服(支付宝的客服还是不错的)

   2.用openssel生产自己(开发者的)私钥以及公钥,具体还是看官网,里面有详细的介绍

   3.上传你生产的公钥到支付宝用于解码我们生产的sign签名,并且复制支付宝的公钥到本地用于解码支付宝返回来的sign签名

   开发流程:

    1.新建一个openerp模块:payment_alipay(具体不同版本可能有些不相同,我用的openerp7)

    wKiom1cot06RBPwiAAAoNWq0OuE270.png

   2.主要处理代码:

     wKiom1cot1mDMd9VAAA5YUPK0yQ047.png

   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签名要求的,以上还是一个开始,因为后面有

wKiom1cot9yAuQuoAAAqNYKqUF4555.png

spacer.gif等等要做开发的。