Апплет WeChat делает платежи WeChat через облачную функцию

Апплет WeChat

Мини-программа WeChat WeChat Pay

Официальная блок-схема выглядит следующим образом:

Апплет WeChat Ссылка на официальную блок-схему оплаты WeChat

avatar

Мой упрощенный процесс:

  1. Локально инициировать запрос заказа для вызова облачной функции и передачи данных
  2. Облачная функция обрабатывает данные и возвращает 5 параметров
  3. Примите 5 параметров локально и инициируйте платежный запрос
  4. закрытие сделки

Основной код:

//第一步,本地发起下单请求并传送数据。这一步,在你的wxml中的某个元素
//中绑定事件<button bindtap='pay'></button>。通过这个pay函数,
//触发云函数并传递一些数据
pay: function(){
	//需要上传给云函数的数据
	let uploadData = {
		//此次需要支付的金额,单位是分。例如¥1.80=180
		"total_fee": "180",
		//用户端的ip地址
		"spbill_create_ip": "123.123.123.123"
	}
	//调用云函数
	wx.cloud.callFunction({
		//云函数的名字,这里我定义为payment
		name: "payment",
		//需要上传的数据
		data: uploadData
	}).then(res => {
		//这个res就是云函数返回的5个参数
		//通过wx.requestPayment发起支付
		wx.requestPayment({
			timeStamp: res.result.data.timeStamp,
			nonceStr: res.result.data.nonceStr,
			package: res.result.data.package,
			signType: res.result.data.signType,
			paySign: res.result.data.paySign,
			success: res => {
				//支付成功
			},
			fail: err => {
				//支付失败
			}
	})
}


Выше весь код на локальной стороне, далее нам нужно только получить код облачной функции для завершения всей работы.

Содержимое облачной функции:

  1. Вызов апплета для входа в API -> Openid
  2. Создать торговый заказ
  3. Вызов единого API заказа -> prepay_id
  4. Подпишите объединенные данные снова, возвращая 5 параметров

Облачная функция, которую я создал, называется платежом, и структура облачной функции должна быть

payment
|__index.js
|__package.json

##Подробная практика каждого шага

1. Вызовите апплет для входа в API -> Openid

этот шагЦель состоит в том, чтобы получить Openid пользователя

Добавьте следующий код в index.js облачной функции.

//获取云实例
const cloud = require('wx-server-sdk')
//云初始化
cloud.init()
//获取微信调用上下文信息,其中包括Openid,Appid等
const wxContext = cloud.getWXContext()
//获取用户openid
const openid = wxContext.OPENID

Здесь мы достигли нашей первой цели.

2. Создать торговый заказ

Документ по разработке платежей WeChat — единый порядок

Цель этого шага состоит в том, чтобыГенерировать заказ, который вызывает API Unified Order. Согласно официальному документу, нам нужны следующие данные:

  1. appid (идентификатор мини-программы)

  2. openid (пользователь OPENID)

  3. mch_id (идентификатор продавца)

  4. nonce_str (случайная строка)

  5. корпус (описание товара)

  6. out_trade_no (номер заказа продавца)

  7. total_fee (сумма прейскурантной цены)

  8. spbill_create_ip (IP-адрес терминала)

  9. notify_url (адрес уведомления)

  10. trade_type (тип сделки)

  11. ключ

  12. подписать

Мы решаем это один за другим.

1. appid

После того, как администратор мини-программы войдет на общедоступную платформу и войдет в систему с учетной записью мини-программы, нажмите «Настройки» в меню слева, а затем нажмите «Настройки разработки», чтобы запросить AppID мини-программы.

пример значенияwxd678efh567hg6787

Добавьте следующий код в index.js облачной функции:

const appid='wxwxd678efh567hg6787'

2. openid

Первая ступенька получена.

пример значенияoUpF8uMuAJO_M2pxb1Q9zNjWeS6o

3. mch_id

Войдите в торговую платформу WeChat Paypay.weixin.qq.com, Нажмите «Центр учетных записей» выше, и «Учетная запись входа» в «Личной информации» будет mch_id.

пример значения1230000109

Добавьте следующий код в index.js облачной функции:

const mch_id='1230000109'

4. nonce_str

Произвольно сгенерированное случайное число, не более 32 бит. Вы можете сами написать функцию.

пример значения5K8264ILTKCH16CQ2502SI8ZNMTM67VS

Я создал новый файл JS (random.js) в облачной функции, чтобы сохранить эту функцию, затем облачная структура следует за функцией

payment
|__index.js
|__package.json
|__random.js

Random.js из которых читается:

function random(){
  var result = ''
  const wordList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
  'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
  '3', '4', '5', '6', '7', '8', '9', '0']
  for(let i=0;i<31;i++){
    result += wordList[Math.round(Math.random()*36)]
  }
  return result
}

module.exports = random()

Затем добавьте следующий код в облачную функцию index.js:

const random = require("random.js")

5. body

Формат: название компании - категория продукта продаж.

пример значения腾讯-游戏

Добавьте следующий код в облачную функцию index.js:

const body = "腾讯-游戏"

6. out_trade_no

Внутренний номер заказа торговой системы, не более 32 символов, только цифры, прописные и строчные буквы _-|* иУникальный под одним и тем же идентификатором продавца. Определяется самостоятельно, рекомендуется использовать текущее время + номер продукта.

пример значения20150806125346

Добавьте следующий код в функцию exports.main в облачной функции index.js:

//这里我只使用了当下时间。只要这个数字不是重复的就可以。
const out_trade_no = Date.parse(new Date()).toString()

7. total_fee

Общая сумма заказа, в центах. Например, в настоящее время необходимо заплатить 6,80 йен, общая_плата составляет 680.

пример значения88

这里需要用到我们上传过来的值,先不管

8. spbill_create_ip

Поддерживает IP-адреса в форматах IPV4 и IPV6. IP-адрес машины, которая вызывает платежный API WeChat.

пример значения123.12.12.123

这里需要用到我们上传过来的值,先不管

9. notify_url

Адрес обратного вызова для асинхронного получения уведомлений о результатах оплаты WeChat. URL-адрес уведомления должен быть URL-адресом, доступным из внешней сети, и не может содержать параметры. Здесь вы можете заполнить URL вашего собственного сервера.

пример значенияhttp://www.weixin.qq.com/wxpay/pay.php

Добавьте следующий код в облачную функцию index.js:

//随便填写个服务器就行,我在使用中没有遇到什么问题
const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php'

10. trade_type

Торговый_тип апплета — JSAPI.

пример значенияJSAPI

Добавьте следующий код в облачную функцию index.js:

const trade_type = 'JSAPI'

11. key

ключ — это ключ, установленный торговой платформой, который устанавливается вами. Путь настройки ключа: WeChat Merchant Platform (pay.weixin.qq.com) --> Настройки учетной записи --> Безопасность API --> Настройки ключа.

пример значения1a79a4d60de6718e8e5b326e338ae533

Добавьте следующий код в облачную функцию index.js:

const key = '1a79a4d60de6718e8e5b326e338ae533'

12. sign

Объедините всю вышеуказанную информацию, кроме ключа, в строку по ASCII-коду имени параметра от большего к меньшему, разделите с помощью & и поместите ключ в конец.

Пример строки:

appid=wxd678efh567hg6787&body=微信-游戏&mch_
id=1230000109&nonce_str=5K8264ILTKCH16CQ2502SI8ZNMTM6
7VS&notify_url=http://www.weixin.qq.com/wxpay/pay.php&
openid=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&out_trade_no=2015080
6125346&spbill_create_ip=123.12.12.123&total_fee=88&trad
e_type=JSAPI&key=1a79a4d60de6718e8e5b326e338ae533

Заглавная буква кода MD5 этой строки — знак. Поскольку указанные выше значения total_fee и spbill_create_ip еще не обработаны, эти данные будут обработаны ниже.

Пример значения кода MD5C380BEC2BFD727A4B6845133519F3AD6

На данный момент структура вашей облачной функции должна быть:

payment
|__index.js
|__package.json
|__random.js

Содержимое index.js должно быть:

//云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const openid = cloud.getWXContext().OPENID
const appid = 'wxwxd678efh567hg6787'
const mch_id = '1230000109'
const random = require('random.js')
const body = "腾讯-游戏"
const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php'
const trade_type = 'JSAPI'
const key = '1a79a4d60de6718e8e5b326e338ae533'

//云函数入口函数
exports.main = async (event, content) => {
	const out_trade_no = Date.parse(new Date()).toString()
}

Далее разбираемся с total_fee и spbill_create_ip, и подписываем то, что не было рассмотрено выше. Среди них total_fee и spbill_create_ip загружаются клиентом.Эти два данных находятся в событии параметра функции входа в облачную функцию, поэтому мы добавляем следующий код в функцию входа в облачную функцию

const total_fee = event.total_fee
const spbill_create_ip = event.spbill_create_ip

Наконец, нам нужно обработать знак и добавить следующий код в функцию входа в облачную функцию в соответствии с правилами, указанными в 12.sign.

let stringA = `appid=${appid}&body=${body}&
mch_id=${mch_id}&nonce_str=${random}&
notify_url=${notify_url}&openid=${openid}&
out_trade_no=${out_trade_no}&
spbill_create_ip=${spbill_create_ip}&
total_fee=${total_fee}&trade_type=${trade_type}&
key=1a79a4d60de6718e8e5b326e338ae533`

Теперь нам нужно зашифровать эту строку с помощью кода MD5, поэтому для выполнения этой задачи нам нужно установить пакет npm. Щелкните правой кнопкой мыши элемент облачной функции, выберите «Открыть в терминале» и введите следующую команду:

npm install --save crypto

После завершения добавьте следующий код в файл записи облачной функции.

const crypto = require("crypto")

Таким образом, мы успешно внедрили криптографический инструментарий шифрования в нашу облачную функцию. Затем нам нужно использовать его в функции входа в облачную функцию, чтобы MD5 зашифровал строку A прямо сейчас. по этому мыlet stringA = ...Добавьте следующий код под этой строкой кода

var sign = crypto.createHash('md5').update(stringA).digest('hex').toUpperCase()

Приведенная выше 11 информация, за исключением ключа, — это все данные, которые нам нужны для вызова единого API заказов.

Теперь нам нужно преобразовать эти данные в формат XML, например:

<xml>
<appid>wxd930ea5d5a258f4f</appid>
<mch_id>10000100</mch_id>
<device_info>1000</device_info>
<body>test</body>
<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>
<sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign>
...
</xml>

Создайте новую функцию requestData.js в облачной функции и напишите следующую функцию, чтобы выполнить задачу преобразования данных в xml.

function requestData(
  appid,
  mch_id,
  nonce_str,
  sign,
  body,
  out_trade_no,
  total_fee,
  spbill_create_ip,
  notify_url,
  trade_type,
  openid
){
  let data = "<xml>"
  data += "<appid>"+appid+"</appid>"
  data += "<mch_id>"+mch_id+"</mch_id>"
  data += "<nonce_str>"+nonce_str+"</nonce_str>"
  data += "<sign>"+sign+"</sign>"
  data += "<body>"+body+"</body>"
  data += "<out_trade_no>"+out_trade_no+"</out_trade_no>"
  data += "<total_fee>"+total_fee+"</total_fee>"
  data += "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"
  data += "<notify_url>"+notify_url+"</notify_url>"
  data += "<trade_type>"+trade_type+"</trade_type>"
  data += "<openid>"+openid+"</openid>"
  data += "</xml>"
  return data
}

module.exports = requestData

В настоящее время структура облачной функции

payment
|__index.js
|__package.json
|__package-lock.json //由npm install产生的文件
|__random.js
|__requestData.js

Нам нужно импортировать файл requestData.js в наш проект. Добавьте следующий код в файл записи облачной функции.

const requestData = require("requestData.js")

Теперь мы можем сгенерировать заказ, который вызывает единый платежный API, и этот dataBody является заказом.

let dataBody = reqData(
    appid,
    mch_id,
    random,
    sign,
    body,
    out_trade_no,
    total_fee,
    spbill_create_ip,
    notify_url,
    trade_type,
    openid
  )

На данный момент мы достигли нашей второй цели.

3. Вызов Unified под одним API -> Prepawe_id

официальная документация

Нужна ссылка на официальнуюhttps://api.mch.weixin.qq.com/pay/unifiedorderИнициировать единый заказ, поэтому здесь нам нужен пакет npm, который поможет нам выполнить запрос запроса, а поскольку возвращаемое значение после инициирования запроса находится в формате xml, нам также нужен пакет npm, который поможет нам проанализировать файл формата xml. Поэтому щелкните правой кнопкой мыши платеж облачной функции, выберите «Открыть в терминале» и введите следующую команду:

npm install --save request
npm install --save xmlreader

Вставьте два вышеупомянутых пакета в файл записи облачной функции:

const request = require("request")
const xmlreader = require("xmlreader")

Затем вы можете инициировать запрос к единому API заказа в функции ввода облачной функции.Поскольку запрос является асинхронным запросом, нам нужно вернуть обещание.

return new Promise(reslove => {
	request({
		//官方统一下单api的url
		url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
		//请求方法,post
		method: "POST",
		//需要传送的订单,就是刚刚我们生成的dataBody
		body: dataBody
	}, body => {
		//body就是我们收到的数据,我们需要得到其中的prepay_id
		//使用xmlreader解析body,获得其中的prepay_id
		xmlreader.read(body, res => {
			//此时我们已经完成第三步的目的了
			let prepay_id = res.xml.prepay_id.text()
		}
	}
}

Третий шаг завершен

4. Снова подпишите объединенные данные и верните 5 параметров.

Известно, что wx.requestPayment() требует пять параметров, которые

  1. timeStamp
  2. nonceStr
  3. package
  4. signType
  5. paySign

Среди них timeStamp — это метка времени, которую можноDate.parse(new Date()).toString()получить. nonceStr — это случайная строка, которую можно получить с помощью нашей функции random. package — это prepay_id, полученный на предыдущем шаге, signType — тип подписи, мы выбрали MD5. Итак, теперь у нас есть только неизвестный paySign, и способ получить paySign таков:

paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K826
4ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx20170
33010242291fcfe0db70013231072&signType=MD5&timeStamp=
1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D
9B4E54AB1950F51E0649E8810ACD6

Так что продолжаем писать в коде предыдущего шага

return new Promise(reslove => {
	request({
		url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
		method: "POST",
		body: dataBody
	}, body => {
		xmlreader.read(body, res => {
			let prepay_id = res.xml.prepay_id.text()
			let timeStamp = Date.parse(new Date()).toString()
			let str = `appId=${appid}&nonceStr=${random}&package=prepay_id=${prepay_id}&signType=MD5&timeStamp=${timeStamp}&key=1a79a4d60de6718e8e5b326e338ae533`
			let paySign = crypto.createHash('md5').update(str).digest('hex')
			//返回上面的五个参数
			reslove({
				data: {
					timeStamp: timeStamp,
            		nonceStr: random,
            		package: `prepay_id=${prepay_id}`,
            		signType: 'MD5',
            		paySign: paySign
            	}
            })
		}
	}
}

На этом процесс оплаты апплета WeChat заканчивается.

В настоящее время структура облачных функций такова:

payment
|__index.js
|__package.json
|__package-lock.json
|__random.js
|__requestData.js

index.js:

//云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const openid = cloud.getWXContext().OPENID
const appid = 'wxwxd678efh567hg6787'
const mch_id = '1230000109'
const random = require('random.js')
const body = "腾讯-游戏"
const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php'
const trade_type = 'JSAPI'
const key = '1a79a4d60de6718e8e5b326e338ae533'
const crypto = require("crypto")
const requestData = require("requestData")
const request = require("request")
const xmlreader = require("xmlreader")

//云函数入口函数
exports.main = async (event, content) => {
    const out_trade_no = Date.parse(new Date()).toString()
    const total_fee = event.total_fee
    const spbill_create_ip = event.spbill_create_ip
    let stringA = `appid=${appid}&body=${body}&mch_id=${mch_id}&nonce_str=${random}&notify_url=${notify_url}&openid=${openid}&out_trade_no=${out_trade_no}&spbill_create_ip=${spbill_create_ip}&total_fee=${total_fee}&trade_type=${trade_type}&key=1a79a4d60de6718e8e5b326e338ae533`
    var sign = crypto.createHash('md5').update(stringA).digest('hex').toUpperCase()
    let dataBody = reqData(
	    appid,
	    mch_id,
	    random,
	    sign,
	    body,
	    out_trade_no,
	    total_fee,
	    spbill_create_ip,
	    notify_url,
	    trade_type,
	    openid
	  )
	return new Promise(reslove => {
	    request({
	        url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
	        method: "POST",
	        body: dataBody
	    }, body => {
	        xmlreader.read(body, res => {
	            let prepay_id = res.xml.prepay_id.text()
	            let timeStamp = Date.parse(new Date()).toString()
	            let str = `appId=${appid}&nonceStr=${random}&package=prepay_id=${prepay_id}&signType=MD5&timeStamp=${timeStamp}&key=1a79a4d60de6718e8e5b326e338ae533`
	            let paySign = crypto.createHash('md5').update(str).digest('hex')
	            //返回上面的五个参数
	            reslove({
	                data: {
	                    timeStamp: timeStamp,
	                    nonceStr: random,
	                    package: `prepay_id=${prepay_id}`,
	                    signType: 'MD5',
	                    paySign: paySign
	                }
	            })
	        }
	    }
}

random.js:

function random(){
  var result = ''
  const wordList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
  'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
  '3', '4', '5', '6', '7', '8', '9', '0']
  for(let i=0;i<31;i++){
    result += wordList[Math.round(Math.random()*36)]
  }
  return result
}

module.exports = random()

requestData.js:

function requestData(
  appid,
  mch_id,
  nonce_str,
  sign,
  body,
  out_trade_no,
  total_fee,
  spbill_create_ip,
  notify_url,
  trade_type,
  openid
){
  let data = "<xml>"
  data += "<appid>"+appid+"</appid>"
  data += "<mch_id>"+mch_id+"</mch_id>"
  data += "<nonce_str>"+nonce_str+"</nonce_str>"
  data += "<sign>"+sign+"</sign>"
  data += "<body>"+body+"</body>"
  data += "<out_trade_no>"+out_trade_no+"</out_trade_no>"
  data += "<total_fee>"+total_fee+"</total_fee>"
  data += "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"
  data += "<notify_url>"+notify_url+"</notify_url>"
  data += "<trade_type>"+trade_type+"</trade_type>"
  data += "<openid>"+openid+"</openid>"
  data += "</xml>"
  return data
}

module.exports = requestData