>위챗 애플릿 >위챗 개발 >WeChat Pay의 개발 과정에 대한 자세한 소개

WeChat Pay의 개발 과정에 대한 자세한 소개

高洛峰
高洛峰원래의
2017-03-15 17:33:372035검색

본 글은 주로 위챗 결제 개발 과정에 대한 관련 정보를 소개하고 있으니, 필요하신 분들은 참고하시면 됩니다.

참고로 저는 모바일 앱과 관련된 위챗 오픈 플랫폼 결제를 사용하고 있습니다. 그리고 그 계정은 대중과 관련이 없습니다.

위챗 결제 주요 동작 과정

1. 사용자가 앱을 탐색한 후 상품을 선택하고 주문합니다.

2. 서버는 주문 로직을 처리하고 정식 결제 프로세스를 시작합니다

3. 먼저 백엔드 서버는 토큰을 얻기 위해 weixin 서버에 요청을 시작합니다.

4. 백엔드 서버는 토큰을 가져와서 다른 매개변수로 암호화한 후 다시 weixin 서버에 요청을 보내 선불 ID를 얻습니다.

5. 백엔드 서버가 선불 ID를 반환합니다. 앱 클라이언트

6. 앱이 휴대폰의 WeChat제어를 호출하여 결제 프로세스를 완료합니다.

7.app은 백엔드 서버에 콜백 요청을 시작하여 서버에 트랜잭션이 완료되었음을 알립니다.

8. weixin 서버는 모든 프로세스를 처리한 후 백엔드 서버에 게시 요청을 시작하여 백엔드 서버에 거래가 완료되었음을 공식적으로 알립니다

위 과정 중 일부 참고:

1. 매번 획득하는 토큰에는 시간 제한이 있으며, 기본값은 7200초이며, 하루에 최대 200회 획득할 수 있으므로 redis캐시에 넣고 만료된 후 다시 검색하는 것이 가장 좋습니다

2. 앱에서 시작된 콜백은 기본적으로 신뢰할 수 없습니다. WeChat 서버에 대한 쿼리 주문을 시작하는 것이 가장 좋지만(반드시 그런 것은 아님), 이 거래의 결과를 확인하세요.

3. weixin 서버에 의해 백그라운드로 시작된 notify는 거래 완료를 보장하는 최종 장벽입니다. 백그라운드 서버는 확인 후 "성공"을 반환해야 합니다. 그렇지 않으면 weixin 서버가 요청을 다시 보내려고 시도합니다.

토큰 가져오기

이 단계는 매우 간단합니다. 가져오기 요청을 보내기만 하면 됩니다. 올바른 매개변수를 구성하기만 하면 됩니다.

rreee

선불 받기

WeChat 결제 개발 과정에서 가장 지루한 일은 선불을 받는 것입니다.

이 단계에서는 다음 매개변수를 어셈블해야 합니다.

‘‘‘从微信服务器获取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请求失败

패키지 어셈블

여기 첫 번째 단계는 패키지를 조립하는 것입니다:

{
"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&notify_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"
}

패키지를 조립하는 데 필요한 매개변수는 위의 코드와 같으므로 다음을 준비해야 합니다. params를 준비한 후 서명 및 서명 프로세스를 다음과 같이 준비합니다.

1. 키의 사전 순서에 따라 params를 정렬한 다음 string으로 연결합니다.

기호를 포함하지 마십시오. 2. 위의 Splice key=paternerKey 에서 문자열 뒤에 md5 서명을 수행한 다음 이를 대문자로 변환합니다. 서명

을 입력하고 모든 매개변수의 값을 urlencode합니다. 이를 변환한 다음 sign=signValue로 연결하여 패키지 문자열을 가져옵니다.

여기서 MD5를 만드는 과정은 다음과 같습니다.

"package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%
95&fee_type=1&input_charset=UTF-8&notify_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",

패키지를 조립하는 코드:

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

계속 매개변수 조립

패키지를 가져온 후 계속해서 매개변수를 조립합니다.

여기에 필요한 매개변수

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

traceid=test_1399514976

여기에 구멍이 있다는 점에 유의하세요.

위의 매개변수 은 서명에 관여하지만 마지막 매개변수에 appKey가 포함되어 있지 않으므로 서명 후 삭제하는 것을 잊지 마세요.

1. 모든 매개변수를 사전순으로 정렬한 후

2. sha1을 서명하고 위 문자열 끝에 연결합니다

3. 여기에서 appKey를 삭제하고 서명을 추가하세요

sha1 서명을 얻는 코드는 다음과 같습니다.

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&notify_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

그러면 다음과 같은 결과가 나옵니다. 매개변수:

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

선불 결제

코드는 다음과 같습니다:

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

우리가 받은 선불 형식은 다음과 같아야 합니다:

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

다시 서명

위의 sha1 서명 방법을 사용하여 다시 서명하고 다음 매개변수를 얻습니다.

‘‘‘获取预支付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

백그라운드 서버는 결과를 앱에 반환합니다. 이때 앱은 결제를 시작할 수 있습니다.

위 프로세스 코드는 다음과 같습니다.

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

백그라운드 비동기 알림

WeChat 서버에서 전송 비동기 알림은 결제 성공의 최종 신호입니다. 이 단계는 안전입니다. 안전을 위해 서명을 확장해야 합니다.

확장 코드는 다음과 같습니다.

‘‘‘接收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‘

    ‘‘‘获取token‘‘‘
    access_token = weixinRequestHandler.getAccessToken()
    logging.info("get token: %s" % access_token)
    if access_token:
      ‘‘‘设置package参数‘‘‘
      packageParams = {}
      packageParams[‘bank_type‘] = ‘WX‘  #支付类型
      packageParams[‘body‘] = product_name #商品名称
      packageParams[‘fee_type‘] = ‘1‘   #人民币 fen
      packageParams[‘input_charset‘] = ‘GBK‘ #GBK
      packageParams[‘notify_url‘] = config[‘notify_url‘] #post异步消息通知
      packageParams[‘out_trade_no‘] = str(out_trade_no) #订单号
      packageParams[‘partner‘] = config[‘partnerId‘] #商户号
      packageParams[‘total_fee‘] = str(int(order_price*100))  #订单金额,单位是分
      packageParams[‘spbill_create_ip‘] = remote_addr #IP
      packageParams[‘time_start‘] = order_create_time #订单生成时间
      packageParams[‘time_expire‘] = order_deadline #订单失效时间

      ‘‘‘获取package‘‘‘
      package = weixinRequestHandler.getPackage(packageParams)

      ‘‘‘设置支付参数‘‘‘
      signParams = {}
      signParams[‘appid‘] = config[‘appId‘]
      signParams[‘appkey‘] = config[‘paySignKey‘] #delete
      signParams[‘noncestr‘] = noncestr
      signParams[‘package‘] = package
      signParams[‘timestamp‘] = timestamp
      signParams[‘traceid‘] = ‘mytraceid_001‘

      ‘‘‘生成支付签名‘‘‘
      app_signature = weixinRequestHandler.createSHA1Signature(signParams)
      ‘‘‘增加不参与签名的额外参数‘‘‘
      signParams[‘sign_method‘] = ‘sha1‘
      signParams[‘app_signature‘] = app_signature

      ‘‘‘剔除appKey‘‘‘
      del signParams[‘appkey‘]

      ‘‘‘获取prepayid‘‘‘
      prepayid = weixinRequestHandler.gerPrepayId(access_token, signParams)
      if prepayid:

        ‘‘‘使用拿到的prepayid再次准备签名‘‘‘
        pack = ‘sign=WXPay‘
        prepayParams = {}
        prepayParams[‘appid‘] = config[‘appId‘]
        prepayParams[‘appkey‘] = config[‘paySignKey‘]
        prepayParams[‘noncestr‘] = noncestr
        prepayParams[‘package‘] = pack
        prepayParams[‘partnerid‘] = config[‘partnerId‘]
        prepayParams[‘prepayid‘] = prepayid
        prepayParams[‘timestamp‘] = timestamp

        ‘‘‘生成签名‘‘‘
        sign = weixinRequestHandler.createSHA1Signature(prepayParams)

        ‘‘‘准备输出参数‘‘‘
        returnParams = {}
        returnParams[‘status‘] = 0
        returnParams[‘retmsg‘] = ‘success‘
        returnParams[‘appid‘] = config[‘appId‘]
        returnParams[‘noncestr‘] = noncestr
        returnParams[‘package‘] = pack
        returnParams[‘prepayid‘] = prepayid
        returnParams[‘timestamp‘] = timestamp
        returnParams[‘sign‘] = sign
        returnParams[‘partnerId‘] = config[‘partnerId‘]
        returnParams[‘addtion‘] = addtion
        
      else:
        ‘‘‘prepayid获取失败‘‘‘
        returnParams = {}
        returnParams[‘status‘] = -1
        returnParams[‘retmsg‘] = ‘prepayid获取失败‘
    else: 
      ‘‘‘token获取失败‘‘‘
      returnParams = {}
      returnParams[‘status‘] = -1
      returnParams[‘retmsg‘] = ‘token获取失败‘

    ‘‘‘生成json格式文本,然后返回给APP‘‘‘
    return returnParams

전체 과정은 다음과 같습니다.

def isTenpaySign(self, params):
    helper = WeixinRequestHandler()
    sign = helper.createMD5Signature(params)
    return params[‘sign‘] == sign

마지막으로 사용자가 휴대폰으로 결제를 완료하면 결제가 성공한 것으로 간주되지 않습니다. 이때 Weixin 서버가 알림 알림을 통해 반환된 성공을 받아야만 거래가 성공한 것으로 간주됩니다. 공식 WeChat 메시지를 받을 수 있습니다.

위는 WeChat 결제 개발 과정에 대한 정보를 모아 놓은 것입니다. 앞으로도 관련 정보를 계속 추가하겠습니다.

위 내용은 WeChat Pay의 개발 과정에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.