微信支付的开发流程详解

最近在公司做了微信支付的接入,这里总结下开发的一些经验

注意,我使用的是微信开放平台的支付,与手机app相关,而与公众账号无关。

微信支付的主要操作流程

1.用户浏览app,选定商品然后下单。

2.服务器处理订单逻辑,开始正式发起支付流程

3.首先,后台服务器向weixin服务器发起请求,获取一个token。

4.后台服务器拿到token,使用和其他参数加密,再次向weixin服务器发起请求,获取一个预支付prepayid

5.后台服务器将该prepayid返回给app客户端

6.app调用手机上的微信控件,完成付款流程。

7.app向后台服务器发起一个回调请求,通知服务器交易完成。

8.weixin服务器处理完所有的流程后,向后台服务器发起一个post请求,正式通知后台服务器交易完毕

上面流程的一些注意点:

1.每次获取的token是有时效的,认是7200s,而且每天最多获取200次,因此最好放到redis中缓存起来,等失效后再去重新获取

2.app发起的回调认是不可靠的,后台应该尽可能(不是必须)向微信服务器发起订单查询查询本次交易的结果。

3.weixin服务器向后台发起的notify,才是确保交易完成的最后屏障。后台服务器确认后必须返回“success”,否则weixin服务器会尝试重发请求。

获取token

这步很简单,发送一个get请求即可。只需配置正确参数。

rush:PHP;"> ‘‘‘从微信服务器获取token‘‘‘ def _getAccesstokenFromWeixin(self): response = requests.get(self.tokenUrl % (self.appId,self.appSecret)) if response.status_code == 200: text = response.text tokenInfo = json.loads(text) try: token = tokenInfo[‘access_token‘] expires_in = tokenInfo[‘expires_in‘] self._writeWeixinTokenLog(token,self.order_no) return token except KeyError: return None #token获取失败 return None #http请求失败

获取prepayid

在微信支付的开发流程中,最繁琐的就是获取prepayid。

这一步我们需要组装这样一个参数:

rush:PHP;"> { "appid":"wxd930ea5d5a258f4f","traceid":"test_1399514976","noncestr":"e7d161ac8d8a76529d39d9f5b4249ccb ","timestamp":1399514976,"package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF% 95&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_Trade_ no=7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1& total_fee=1&sign=7F77B507B755B3262884291517E380F8","sign_method":"sha1","app_signature":"7f77b507b755b3262884291517e380f8" }

组装package

这里的第一步就是组装package:

rush:PHP;"> "package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF% 95&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_Trade_ no=7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1& total_fee=1&sign=7F77B507B755B3262884291517E380F8",

组装package需要的参数如上面代码所示,所以我们需要准备一个params,然后准备签名,签名流程如下:

1.按照key的字典序,对params进行排序,然后拼接成字符串,注意这些key不包括sign

2.在上面的字符串后面拼接key=paternerKey,然后对整个字符串进行md5签名,然后转换成大写,此时我们就得到了签名

然后我们将所有params的value进行urlencode转码,然后后面拼接上sign=signValue,就得到了package字符串。

这里创建MD5的过如下:

rush:PHP;"> def createMD5Signature(self,signParams): ‘‘‘先排序‘‘‘ sortedParams = sorted(signParams.iteritems(),key=lambda d:d[0]) ‘‘‘拼接‘‘‘ stringSignTemp = "&".join(["%s=%s" % (item[0],item[1]) for item in sortedParams if item[0] != ‘sign‘ and ‘‘ != item[1]]) #加上财付通商户权限密钥 stringSignTemp += ‘&key=%s‘ % (self.partnerKey) #使用MD5进行签名,然后转化为大写 stringSign = hashlib.md5(stringSignTemp).hexdigest().upper() #Upper return stringSign

组装package的代码

rush:PHP;"> def getPackage(self,packageParams): ‘‘‘先获取params的sign,然后将params进行urlencode,最后拼接,加上sign‘‘‘ sign = self.createMD5Signature(packageParams) packageParams = sorted(packageParams.iteritems(),key=lambda d:d[0]) stringParams = "&".join(["%s=%s" % (item[0],urllib.quote(str(item[1]))) for item in packageParams]) stringParams += ‘&sign=%s‘ % (sign) return stringParams

继续组装参数

得到package后,我们继续组装参数:

这里需要的参数为:

rush:PHP;"> appid=wxd930ea5d5a258f4f appkey=L8LrMqqeGRxST5reouB0K66CaY A WpqhA Vsq7ggKkxHCOastWksvuX1uvmvQcl xaHoYd3ElNBrNO2DHnnzgfVG9Qs473M3DTOZug5er46FhuGofumV8H2FVR9qkjSlC5K noncestr=e7d161ac8d8a76529d39d9f5b4249ccb package=bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%95 &fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_Trade_no =7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1&tot al_fee=1&sign=7F77B507B755B3262884291517E380F8 timestamp=1399514976

traceid=test_1399514976

注意这里有个坑:

参与签名的是上面的参数,但是最后的参数中不包括appKey,签名后要记得删除

1.所有参数按照字典序排序,然后拼接

2.进行sha1签名,拼接到上面字符串的后面

3.注意这里要删除appKey,然后加上sign

获取sha1签名的代码如下:

rush:PHP;"> def createSHA1Signature(self,params): ‘‘‘先排序,然后拼接‘‘‘ sortedParams = sorted(params.iteritems(),key=lambda d:d[0]) stringSignTemp = "&".join(["%s=%s" % (item[0],item[1]) for item in sortedParams]) stringSign = hashlib.sha1(stringSignTemp).hexdigest() return stringSign

随后我们获取到这样的参数:

rush:PHP;"> { "appid":"wxd930ea5d5a258f4f","noncestr":"e7d161ac8d8a76529d39d9f5b4249ccb","package":"Sign=WXpay"; "partnerid":"1900000109" "prepayid":"1101000000140429eb40476f8896f4c9","sign":"7ffecb600d7157c5aa49810d2d8f28bc2811827b","timestamp":"1399514976" }

获取prepayid

代码如下:

rush:PHP;"> ‘‘‘获取预支付prepayid‘‘‘ def gerPrepayId(self,token,requestParams): ‘‘‘将参数,包括package,进行json化,然后发起post请求‘‘‘ data = json.dumps(requestParams) response = requests.post(self.gateUrl % (token),data=data) if response.status_code == 200: text = response.text text = json.loads(text) errcode = text[‘errcode‘] if errcode == 0: return text[‘prepayid‘] return None

我们获取的prepayid格式应该是这样:

{"prepayid":"1101000000140429eb40476f8896f4c9","errcode":0,"errmsg":"Success"}

再次签名

这里采用上面sha1的签名方式再次签名,获取到下面的参数:

rush:PHP;"> { "appid":"wxd930ea5d5a258f4f","timestamp":"1399514976" }

后台服务器将该结果返回给app,此时app即可发起支付。

上面的流程代码为:

rush:PHP;"> ‘‘‘接收app的请求,返回prepayid‘‘‘ class WeixinRequirePrePaidHandler(BasicTemplateHandler):

‘‘‘这个方法在OrdersAddHandler中被调用‘‘‘
@staticmethod
def getPrePaidResult(order_no,total_pay,product_name,client_ip):
‘‘‘封装了常用的签名算法‘‘‘
weixinRequestHandler = WeixinRequestHandler(order_no)
‘‘‘收集订单相关信息‘‘‘
addtion = str(random.randint(10,100)) #产生一个两位的数字,拼接在订单号的后面
out_Trade_no = str(order_no) + addtion
order_price = float(total_pay) #这里必须允许浮点数,后面转化成分之后转化为int

order_price = 0.01 #测试

remote_addr = client_ip #客户端的IP地址
print remote_addr
current_time = int(time.time())
order_create_time = str(current_time)
order_deadline = str(current_time + 20*60)

‘‘‘这里的一些参数供下面使用‘‘‘
noncestr = hashlib.md5(str(random.random())).hexdigest()
timestamp = str(int(time.time()))
pack = ‘Sign=WXPay‘

‘‘‘<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>token‘‘‘
access_token = weixinRequestHandler.getAcce<a href="https://www.jb51.cc/tag/sst/" target="_blank" class="keywords">sst</a>oken()
logging.info("get token: <a href="https://www.jb51.cc/tag/s/" target="_blank" class="keywords">%s</a>" % access_token)
if access_token:
  ‘‘‘设置package参数‘‘‘
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms = {}
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘bank_type‘] = ‘WX‘  #支付类型
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘body‘] = product_name #商品<a href="https://www.jb51.cc/tag/mingcheng/" target="_blank" class="keywords">名称</a>
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘fee_type‘] = ‘1‘   #人民币 fen
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘input_charset‘] = ‘GBK‘ #GBK
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘notify_url‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘notify_url‘] #post异步消息<a href="https://www.jb51.cc/tag/tongzhi/" target="_blank" class="keywords">通知</a>
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘out_<a href="https://www.jb51.cc/tag/Trad/" target="_blank" class="keywords">Trad</a>e_no‘] = str(out_<a href="https://www.jb51.cc/tag/Trad/" target="_blank" class="keywords">Trad</a>e_no) #订单号
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘partner‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘partnerId‘] #商户号
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘total_fee‘] = str(int(order_price*100))  #订单金额,单位是分
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘spbill_create_ip‘] = remote_addr #IP
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘time_start‘] = order_create_time #订单<a href="https://www.jb51.cc/tag/shengcheng/" target="_blank" class="keywords">生成</a>时间
  packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘time_expire‘] = order_deadline #订单失效时间

  ‘‘‘<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>package‘‘‘
  package = weixinRequestHandler.getPackage(packageP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms)

  ‘‘‘设置支付参数‘‘‘
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms = {}
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘appid‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘appId‘]
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘appkey‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘paySignKey‘] #delete
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘noncestr‘] = noncestr
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘package‘] = package
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘timestamp‘] = timestamp
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘traceid‘] = ‘mytraceid_001‘

  ‘‘‘<a href="https://www.jb51.cc/tag/shengcheng/" target="_blank" class="keywords">生成</a>支付签名‘‘‘
  app_signature = weixinRequestHandler.createSHA1Signature(signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms)
  ‘‘‘<a href="https://www.jb51.cc/tag/zengjia/" target="_blank" class="keywords">增加</a>不参与签名的额外参数‘‘‘
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘sign_method‘] = ‘sha1‘
  signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘app_signature‘] = app_signature

  ‘‘‘剔除appKey‘‘‘
  del signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘appkey‘]

  ‘‘‘<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>prepayid‘‘‘
  prepayid = weixinRequestHandler.gerPrepayId(access_token,signP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms)
  if prepayid:

    ‘‘‘使用拿到的prepayid再次准备签名‘‘‘
    pack = ‘sign=WXPay‘
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms = {}
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘appid‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘appId‘]
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘appkey‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘paySignKey‘]
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘noncestr‘] = noncestr
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘package‘] = pack
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘partnerid‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘partnerId‘]
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘prepayid‘] = prepayid
    prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘timestamp‘] = timestamp

    ‘‘‘<a href="https://www.jb51.cc/tag/shengcheng/" target="_blank" class="keywords">生成</a>签名‘‘‘
    sign = weixinRequestHandler.createSHA1Signature(prepayP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms)

    ‘‘‘准备<a href="https://www.jb51.cc/tag/shuchu/" target="_blank" class="keywords">输出</a>参数‘‘‘
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms = {}
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘status‘] = 0
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘retmsg‘] = ‘success‘
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘appid‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘appId‘]
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘noncestr‘] = noncestr
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘package‘] = pack
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘prepayid‘] = prepayid
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘timestamp‘] = timestamp
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘sign‘] = sign
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘partnerId‘] = con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>[‘partnerId‘]
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘addtion‘] = addtion

  else:
    ‘‘‘prepayid<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>失败‘‘‘
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms = {}
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘status‘] = -1
    returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘retmsg‘] = ‘prepayid<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>失败‘
else: 
  ‘‘‘token<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>失败‘‘‘
  returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms = {}
  returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘status‘] = -1
  returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘retmsg‘] = ‘token<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>失败‘

‘‘‘<a href="https://www.jb51.cc/tag/shengcheng/" target="_blank" class="keywords">生成</a>json格式文本,然后返回给APP‘‘‘
return returnP<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms

后台异步通知

微信服务器发来的notify异步通知,才是支付成功的最终标志,这一步处于安全起见,我们必须进行延签:

延签代码如下:

rush:PHP;"> def isTenpaySign(self,params): helper = WeixinRequestHandler() sign = helper.createMD5Signature(params) return params[‘sign‘] == sign

整体流程如下:

rush:PHP;"> ‘‘‘微信服务器向后台发送的异步通知‘‘‘ class WeixinAppNotifyHandler(BasicTemplateHandler): def initialize(self): self.weixinResponseHandler = WeixinResponseHandler()

def post(self):
‘‘‘解析参数‘‘‘
params = self.parseQueryString()

‘‘‘验证是否是weixin服务器发回的消息‘‘‘
verifyWeixinSign = self.weixinResponseHandler.isTenpaySign(p<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms)
‘‘‘处理订单‘‘‘
if verifyWeixinSign:
  ‘‘‘订单逻辑‘‘‘
  order_no = str(p<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms[‘out_<a href="https://www.jb51.cc/tag/Trad/" target="_blank" class="keywords">Trad</a>e_no‘])
  order_no = order_no[0:-2]
  print ‘<a href="https://www.jb51.cc/tag/s/" target="_blank" class="keywords">%s</a> paied successfully‘ % order_no
  self.saveWeixinReceipt(p<a href="https://www.jb51.cc/tag/ara/" target="_blank" class="keywords">ara</a>ms)
  updateOrdersPaidByWeixin(order_no) #更新订单使用状态
  consumeCouponByOrderNo(order_no) #优惠券已经使用
  self.write("success")
else:
  self.write("fail")

def parseQueryString(self):
‘‘‘获取url中所有的参数‘‘‘
uri = self.request.uri
‘‘‘解析出URI中的query字符串‘‘‘
parseResult = urlparse.urlparse(uri)
query = parseResult.query
‘‘‘解析query字符串‘‘‘
params = urlparse.parse_qs(query)
for item in params:
params[item] = params[item][0].strip()
return params

最后说明一点,用户在手机上付完款,并不算支付成功,只有weixin服务器收到notify通知返回的success时,才算交易最终成功,此时我们的手机可以收到微信官方发来的一条消息。

以上就是对微信支付开发流程的资料整理,后续继续补充相关资料,谢谢大家对本站的支持

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...