创建微信小程序订单的ngnix配置

对于 POST/tuo-fair-backend/wx-mini-trade/create-order 这个接口,需要在tuo-fair-backend的代理拦截中,在
auth_request /tuo-account/authenticates/check;
之后加上这些配置:

auth_request_set $auth_openid $upstream_http_x_wx_openid;
auth_request_set $auth_union $upstream_http_x_wx_unionid;
auth_request_set $auth_session_key $upstream_http_x_wx_session_key;

proxy_set_header X-Wx-Openid $auth_openid;
proxy_set_header X-Wx-Unionid $auth_union;
proxy_set_header X-Wx-Session-Key $auth_session_key;



微信支付证书相关

我们在【商户平台】->【自助服务】中设立了API证书及密钥 专区,包括了商户证书如何升级,申请,下载等问题的解答。下面是商户技术人员可能关心的一些问题。

什么是私钥?什么是证书?

数字签名通常定义了两种运算: 签名和验签。发送者用自己的私钥对消息的哈希值进行签名,接收者用对方的公钥进行验签。因此,在使用数字签名时,需要通信的双方都要事先生成公钥、私钥,并且完成双方的公钥交换。其中,私钥是只能由拥有者使用的不公开密钥,公钥是可以公开的密钥。

由于公钥本身并不含有拥有者的身份信息,使用时无法确认它是真实有效的。所以需要证书认证机构(简称 CA)在核实公钥拥有者的信息后,将公钥拥有者的身份信息(如商户号、公司名称等),公钥、签发者信息、有效期以及扩展信息等进行签名,制作成“证书”。

如何获取API证书?

请参考什么是API证书?如何获取API证书?

什么是商户证书?什么是平台证书?

  • “商户证书”是指由商户申请的,包含商户的商户号、公司名称、公钥信息的证书。
  • ”平台证书”是指由微信支付负责申请的,包含微信支付平台标识、公钥信息的证书。

商户在调用 API 时用自身的私钥签名,微信支付使用商户证书中的公钥来验签。微信支付在响应的报文中使用自身的私钥签名,商户使用平台证书中的公钥来验签。

为什么微信支付APIv3要用第三方CA的证书?

主要是为了符合监管的要求,保证更高的安全级别。《中华人民共和国电子签名法》、《金融电子认证规范》及《非银行支付机构网络支付业务管理办法》中规定 “电子签名需要第三方认证的,由依法设立的电子认证服务提供者提供认证服务。”,所以需使用第三方 CA 来确保数字证书的唯一性、完整性及交易的不可抵赖性。

什么是证书序列号

每个证书都有一个由CA颁发的唯一编号,即证书序列号。

如何查看证书序列号?

登陆商户平台【API安全】->【API证书】->【查看证书】,可查看商户API证书序列号。

商户API证书和微信支付平台证书均可以使用第三方的证书解析工具,查看证书内容。或者使用openssl命令行工具查看证书序列号。

$ openssl x509 -in 1900009191_20180326_cert.pem -noout -serial
serial=1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C

如何在程序中加载证书

推荐使用微信支付提供的SDK。你也可以查看下列编程语言的示例代码。

/**
* Java
* 获取证书。
*
* @param filename 证书文件路径  (required)
* @return X509证书
*/
public static X509Certificate getCertificate(String filename) throws IOException {
InputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);

try {
  CertificateFactory cf = CertificateFactory.getInstance("X509");
  X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
  cert.checkValidity();
  return cert;
  } catch (CertificateExpiredException e) {
    throw new RuntimeException("证书已过期", e);
  } catch (CertificateNotYetValidException e) {
    throw new RuntimeException("证书尚未生效", e);
  } catch (CertificateException e) {
    throw new RuntimeException("无效的证书文件", e);
  } finally {
    bis.close();
  }
}

为什么平台证书只提供API下载?

主要是为了确保在更换平台证书时,不影响商户使用微信支付的各种功能。以下场景中,微信支付会更换平台证书:

  • 证书到期后,必须更换。(目前是五年)
  • 证书到期前,例行更换。(每年一次)

为什么平台证书下载接口返回的平台证书需要加密?

主要是为了防御“中间人攻击”。

由于验证应答报文的签名和加密敏感信息时,必须使用到平台证书。平台证书是商户认证微信支付身份时最关键的要素。因此,要通过签名和加密等多重机制来保障商户获取的到平台证书没有被“中间人”篡改。

商户在调用下载接口获取平台证书时,应进行以下四步操作,以保证证书的真实性:

  • 使用与平台共享的对称密钥,解密报文中的证书(必须)
  • 通过解密得到的证书,来验证报文的签名(必须)
  • 使用证书查看工具,核对证书的颁发者为Tenpay.com Root CA。详见如何区分API证书的类型?(强烈推荐)
  • 通过证书信任链验证平台证书(强烈推荐)

如何通过证书信任链验证平台证书?

下面介绍如何使用openssl工具,通过证书信任链验证平台证书。

首先,从微信支付商户平台下载平台证书信任链 CertTrustChain.p7b ,并将它转换为pem证书格式。

openssl pkcs7 -print_certs -in CertTrustChain.p7b -inform der -out CertTrustChain.pem

然后,-CAfile file指定受信任的证书,验证下载的平台证书

openssl verify -verbose -CAfile ./CertTrustChain.pem ./WeChatPayPlatform.pem 

如何通过证书信任链验证平台证书?

商户上送敏感信息时使用了微信支付平台公钥加密。为了能使用正确的密钥解密,微信支付要求商户在请求的HTTP头部中包括证书序列号 ,以声明加密所用的密钥对和平台证书。详见这里的说明。

小程序的开发过程

1.申请小程序

2. 使用云函数开发小程序

1)获取openid

// 初始化云函数
cloud.init({
  // API 调用都保持和云函数当前所在环境一致
  env: cloud.DYNAMIC_CURRENT_ENV
})
cloud.getWXContext() // 获取context, 里面包含了OPENID等信息

2)小程序支付

小程序后台要先完成商户号关联和微信认证,然后需要在IDE中设置好商户号

3)统一下单
官方文档: https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/open/pay/CloudPay.unifiedOrder.html

cloud.cloudPay.unifiedOrder(orderInfo) // order参数是一个object

这里有几点需要注意:
a) 参数orderInfo.functionName,是微信支付完成后微信平台需要调用的函数名,是云函数,需要开发,必须返回{errcode: 0, errmsg: 'SUCCESS'},否则微信平台会反复调用它,它需要拥有处理重复消息的能力。另外使用云函数开发,不会有签名字符串返回,无法做签名校验。
b) orderInfo. subMchId 是关联的商户号,不一定是子商户号
c) orderInfo.spbillCreateIp 是服务器的公网IP地址

3. 云函数的返回问题

云函数默认是1秒返回,可以调整。当使用了setTimeout之类的函数,setTimeout内的函数在运行完成之前,如果先return了结果前端可以收到返回值,但是由于云函数还在运行,若超时,云函数还是会抛出一个超时错误

小程序云开发证书与域名不匹配的问题

Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: tuocad.com. is not in the cert's altnames: DNS:www.jinghuke.com, DNS:jinghuke.com

上面的错误大致是说访问的域名和ssl证书绑定的域名部匹配。

实际开发过程中,我访问的域名是: https://www.tuocad.com

去后台域名配置中查找了对应的配置,发现站点的ip映射到了2个域名,其中一个是www.jinghuke.com。并且,配置了这样的域名访问的映射规则:若直接通过ip地址访问,则映射到www.jinghuke.com;若通过https://www.tuocad.com则还是https://www.tuocad.com。推测是腾讯云平台在访问https://www.tuocad.com时,先作了ip地址转化,然后通过ip地址调用接口,这样就映射到了www.jinghuke.com,导致了错误。