подготовка к разработке
Справочная документацияДокументация по разработке платежей JSAPI
способ оплаты
В настоящее время основные способы оплаты WeChat следующие:
Способ | инструкция |
---|---|
Платежный код платежа | Оплата кодом платежа — это режим, в котором пользователь отображает «проведите штрих-код карты/QR-код» в кошельке WeChat в систему продавца, чтобы отсканировать и завершить платеж напрямую. Он в основном используется в офлайн-сценариях с кассой лицом к лицу. |
Родной платеж | Собственный платеж — это режим, в котором торговая система генерирует платежный QR-код в соответствии с соглашением об оплате WeChat, а затем пользователь использует WeChat для «сканирования» для завершения платежа. Этот режим подходит для оплаты на веб-сайте ПК, оплаты отдельного продукта или заказа в физическом магазине, оплаты рекламы в СМИ и других сценариев. |
JSAPI-платеж | Платеж JSAPI означает, что пользователь открывает страницу продавца H5 в WeChat, и продавец вызывает модуль оплаты WeChat для завершения платежа, вызывая интерфейс JSAPI, предоставляемый платежом WeChat на странице H5. |
Оплата через приложение | Платеж через приложение, также известный как мобильный платеж, представляет собой режим, в котором продавцы используют платежный модуль WeChat для совершения платежа путем интеграции открытого SDK в мобильное приложение APP. |
Платеж H5 | Платеж H5 — это в основном платежный продукт, который вызывает оплату WeChat через браузеры на мобильных устройствах, таких как мобильные телефоны и планшеты. |
Оплата мини-программы | Платеж по мини-программе — это платежный продукт, специально предназначенный для использования в мини-программах. В настоящее время оплату через WeChat можно вызвать только посредством небольшой программы оплаты в апплете. |
Поскольку я уже делал статью об официальной учетной записи, вот основное введение в платеж JSAPI, и последующее развитие также связано с этим.
Сценарии применения JSAPI:
- Пользователь входит в официальную учетную запись продавца в официальной учетной записи WeChat, открывает домашнюю страницу и завершает платеж.
- Друзья пользователя делятся ссылкой на страницу продавца в кругу друзей, окне чата и т. д. Пользователь щелкает ссылку, чтобы открыть страницу продавца и завершает платеж.
- Преобразуйте страницу продавца в QR-код, и пользователь сканирует QR-код и открывает страницу в браузере WeChat для завершения платежа.
основное существительное
В отличие от тестовой разработки общедоступной учетной записи WeChat, она может использовать проникновение во внутреннюю сеть и обычные тестовые учетные записи. Оплата через WeChat требует от разработчиков наличия проверенного реального аккаунта мерчанта, при этом в аккаунте мерчанта включена функция оплаты, а у мерчанта есть реальный официальный аккаунт и т.д.
-
【Торговая платформа WeChat】 Платформа WeChat Merchant представляет собой набор функций продавца, связанных с оплатой WeChat, включая настройку параметров, запрос платежных данных и статистику, онлайн-возврат, денежный купон или операцию мгновенной скидки и другие функции. Вход на платформу:pay.weixin.qq.com.
-
【Общественная платформа микроканала】 Общедоступная платформа WeChat — это портал приложений и фон управления общедоступными учетными записями WeChat. Продавцы могут предоставлять базовую информацию, деловую информацию и финансовую информацию на общедоступной платформе, чтобы подать заявку на платежную функцию WeChat. Вход на платформу:mp.weixin.qq.com.
-
【Платежная система WeChat】 Платежная система WeChat относится к общему термину для API-интерфейса, фоновой системы бизнес-обработки, системы учета, обратного уведомления и других систем, участвующих в завершении платежного процесса WeChat.
-
【Сертификат продавца】 Сертификат продавца представляет собой двоичный файл, предоставляемый WeChat. Когда система продавца инициирует запрос на связь с фоновым сервером платежей WeChat, он используется в качестве учетных данных для фонового платежа WeChat для идентификации истинной личности продавца.
-
【Продавец за кулисами】 Серверная система продавца — это общий термин для внутренних бизнес-систем продавца, таких как: веб-сайт продавца, кассовая система, система выставления счетов, система доставки, система обслуживания клиентов и т. д., обычно связанные с собственной базой данных разработчика.
-
【подписать】 Серверная часть продавца и платежная система WeChat генерируют результат на основе одного и того же ключа и алгоритма для проверки подлинности личности обеих сторон. Алгоритм подписи разработан и опубликован WeChat Pay.Обычно используемые методы подписи: MD5, SHA1, SHA256, HMAC и т. д.
-
【Платежный пароль】 Платежный пароль — это пароль, устанавливаемый отдельно, когда пользователь активирует платеж WeChat, который используется для подтверждения платежа и завершения авторизации транзакции. Этот пароль отличается от пароля для входа в WeChat.
-
【Опенид】 Личность пользователя в официальном аккаунте.Разные официальные аккаунты имеют разные openid. Фоновая система продавца может получить openid пользователя через API, такие как авторизация входа, уведомление об оплате и запрос заказа. Основная цель — оценить одного и того же пользователя и отправить пользователю сообщения службы поддержки, шаблонные сообщения и т. д.
Основные параметры аккаунта приложения:
Описание параметра учетной записи
Параметры в письме | Имя параметра API | Подробное описание |
---|---|---|
APPID | appid | Appid — это уникальный идентификатор общедоступной учетной записи WeChat или приложения с открытой платформой.После подачи заявки на общедоступную учетную запись на официальной платформе или подачи заявки на учетную запись APP на открытой платформе WeChat автоматически назначит соответствующий appid для идентификации приложения. Его можно просмотреть на общедоступной платформе WeChat-->Разработка-->Базовая конфигурация, и значение этого поля также будет включено в электронное письмо продавца с подтверждением платежа WeChat. |
Идентификатор продавца WeChat Pay | mch_id | После того, как продавец подаст заявку на WeChat Pay, платежный счет продавца будет назначен WeChat Pay. |
ключ API | key | Ключ для подписи, сгенерированный в процессе транзакции, хранится только в системе продавца и платежном фоне WeChat и не будет распространяться в сети. Продавцы должны правильно хранить ключ, не передавать его по сети и не хранить в других клиентах, чтобы исключить утечку ключа. Продавцы могут войти в WeChat Merchant Platform в соответствии с подсказкой по электронной почте для настройки. Его также можно установить по следующему пути: Платформа WeChat Merchant (pay.weixin.qq.com) --> Центр учетных записей --> Настройки учетной записи --> Безопасность API --> Основные настройки. |
Appsecret | secret | AppSecret — это пароль интерфейса, соответствующий APPID, используемый дляПолучить учетные данные вызова APIИспользуйте, когда access_token. При оплате WeChat первый проходИнтерфейс OAuth2.0Получить openid пользователя.Этот openid используется для интерфейса заказа в режиме веб-платежей в WeChat. Вы можете войти на общедоступную платформу --> WeChat Pay, чтобы получить AppSecret (вы должны быть разработчиком, и учетная запись не имеет ненормального статуса). |
правила протокола
Продавцы, получающие доступ к WeChat Pay и вызывающие API, должны соблюдать следующие правила:
способ передачи | Для обеспечения безопасности транзакций для передачи используется протокол HTTPS. |
---|---|
Как отправить | Отправить методом POST |
Формат данных | Отправленные и возвращенные данные представлены в формате XML, а имя корневого узла — xml. |
Кодировка символов | Унифицированное использование кодировки символов UTF-8 |
Алгоритм подписи | MD5/HMAC-SHA256 |
Требования к подписи | Для запроса и получения данных требуется проверка подписи.Спецификация безопасности — алгоритм подписи |
Требования к сертификату | Сертификат Merchant API требуется для вызова заявки на возврат, отмену заказа, красный конверт и т. д., и в каждом документе интерфейса API есть инструкции. |
Логика суждения | Сначала оцените возврат поля протокола, затем оцените возврат бизнеса и, наконец, оцените статус транзакции. |
Параметры конфигурации кода в разработке (в реальной разработке рекомендуется настраивать прямо в файле свойств, что удобно для переключения окружения)
// 公众号、小程序appid
public static String APP_ID = "xxxxxxxxx";
// AppSecret
public static String SECRET = "xxxxxxxxx";
// 商户号
public static final String MCH_ID = "xxxxxxxxx";
// API密钥
public static final String API_KEY = "xxxxxxxxx";
// 网页授权域名,JSAPI支付授权目录,JS接口安全域名
public static final String AUTH_URL = "xxxxxxxxx";
Вышеуказанные параметры не являются общедоступными. Если у компании есть готовый платежный счет, то лучше, если нет, то боюсь, что его можно будет арендовать только с клада, но отсутствие оных никак не повлияет на раннее развитие бизнеса.
Деловой кардинг
Диаграмма последовательности бизнес-процессов
Для разработчиков, в процессе инициации платежа,
Серверная часть: в основном вызывает три интерфейса в платеже JSAPI: [Единый API заказов], [API уведомления о результате платежа], [API заказа запроса】
внешний интерфейс:
Во внешнем интерфейсе WeChat H5 активирует платеж и предоставляет пользователям кнопку и передачу данных JSON для запуска платежа WeChat.
начать разработку
Строительство проекта
1. Используя структуру SpringBoot+Thymeleaf, см.Официальный аккаунт WeChat Быстрое развитие (2) Создание проекта и пассивный ответ
2. Представьте официальный набор инструментов SDK
Прочитав документ, я обнаружил, что для синтаксического анализа xml, алгоритмов шифрования и других часто используемых методов WeChat напрямую предоставляет полуфабрикаты общих для нас инструментов и методов.Обратите внимание, что это могут быть только полуфабрикаты, и нужны соответствующие изменения производиться при их использовании.
Ссылка на сайт:Загрузка SDK и DEMO, выберите версию JAVA для загрузки и разархивируйте ее
разработка кода
Официальная конфигурация аккаунта
1. Влить информацию официального аккаунта и мерчанта в Бин
@Component
public class WXPayConfigExtend extends WXPayConfig {
private byte[] certData;
private WXPayConfigExtend() throws Exception {
// String certPath = WXPayConstants.APICLIENT_CERT;
// File file = new File(certPath);
// InputStream certStream = new FileInputStream(file);
// this.certData = new byte[(int) file.length()];
// certStream.read(this.certData);
// certStream.close();
}
@Override
public String getAppID() {
return WXPayConstants.APP_ID;
}
@Override
public String getMchID() {
return WXPayConstants.MCH_ID;
}
@Override
public String getKey() {
return WXPayConstants.API_KEY;
}
@Override
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
@Override
public int getHttpConnectTimeoutMs() {
return 2000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
@Override
public IWXPayDomain getWXPayDomain() {
return WXPayDomainSimpleImpl.instance();
}
public String getPrimaryDomain() {
return "api.mch.weixin.qq.com";
}
public String getAlternateDomain() {
return "api2.mch.weixin.qq.com";
}
@Override
public int getReportWorkerNum() {
return 1;
}
@Override
public int getReportBatchSize() {
return 2;
}
}
получить openid
Страница должна предоставить авторизацию веб-страницы для получения openid. Для авторизации веб-страницы WeChat см.:Быстрая разработка официальной учетной записи WeChat (4) Авторизация веб-страницы WeChat
страница:
Страница здесь непосредственно создает статическую страницу с кнопкой, которая может инициировать предоплату: templates/preOrder.html
Он содержит форму для перехода к внутреннему платежному интерфейсу:
<form name=wexinpayment action='http://chety.mynatapp.cc/api/v1/wechat1/placeOrder' method=post target="_blank">
...
Контроллер для переадресации страниц под Thymeleaf:
@Controller
@RequestMapping("/api/v1/wechat1")
public class IndexController {
// 用于thymeleaf环境下,跳转到字符串相应的html页面
@RequestMapping("/{path}")
public String webPath(@PathVariable String path) {
return path;
}
}
Ingress-контроллер для веб-авторизации:
@Controller
@RequestMapping("/api/v1/wechat1")
public class IndexController {
...
@RequestMapping("/index")
public void index(String code, Model model, HttpServletRequest request, HttpServletResponse response) throws IOException {
// 显式授权,获得code
if (code != null) {
JSONObject json = WeChatUtil.getWebAccessToken(code);
WXPayUtil.getLogger().info("code: ",json.toJSONString());
String openid = json.getString(("openid"));
request.getSession().setAttribute("openid", openid);
WXPayUtil.getLogger().info("index openid={}",openid);
// 重定向到预下单页面
response.sendRedirect("preOrder"); // 重定向到预支付页面
} else {
StringBuffer url = RequestUtil.getRequestURL(request);
WXPayUtil.getLogger().info("index 请求路径:{}"+url);
String path = WeChatUtil.WEB_REDIRECT_URL.replace("APPID", WeChatConstants.APP_ID).replace("REDIRECT_URI", url).replace("SCOPE", "snsapi_userinfo");
WXPayUtil.getLogger().info("index 重定向:{}",path);
// 重定向到授权获取code的页面
response.sendRedirect(path);
}
}
}
Запускаем проект и запрашиваем интерфейс:
1. Введите в адресную строку инструмента разработчика WeChat: {авторизованное доменное имя веб-страницы}//api/v1/wechat1/index
2. Подтвердите авторизацию [Согласен], (цель здесь – получение openid, также можно использовать базовый режим тихой авторизации, без отображения быстрой авторизации) и перейдите на страницу предоплаты, как показано на рисунке:
инициировать платеж
Когда пользователь подтвердит заказ на странице предоплаты, будет запрошен интерфейс [/placeOrder], и бизнес вызовет интерфейс WeChat [Unified Order]:
1. Единый класс объекта заказа WeChat
@Setter
@Getter
@ToString
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxOrderEntity {
private String appid;
private String mchId;
private String deviceInfo;
private String nonceStr;
private String sign;
private String body;
private String outTradeNo;
private int totalFee;
private String spbillCreateIp;
private String notifyUrl;
private String tradeType;
private String openid;
}
2. Бизнес-уровень оплаты WeChat
@Service
public class WxBackendServiceImpl {
@Autowired
WXPayConfigExtend wxPayConfigExtend;
// 统一下单
public Map<String, Object> unifiedorder(Model model, HttpServletRequest request) throws Exception {
WXPayUtil.getLogger().info("进入下单控制器...");
Map<String,Object> data = null;
try {
//生成订单编号
WXPay wxpay = new WXPay(wxPayConfigExtend);
WxOrderEntity order = new WxOrderEntity();
double price = 0.01;
String orderName = "xxx--微信支付";
int number = (int)((Math.random()*9)*1000);//随机数
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//时间
String orderNumber = dateFormat.format(new Date()) + number;
String nonceStr = WXPayUtil.generateNonceStr();
String openId = (String) request.getSession().getAttribute("openid");
openId = openId == null ? "o4036jqo2PN9isV6N2FHGRsGRVqg" : openId; // 前一个openid,是chet在xxx公众号下的openid
order.setBody(orderName);
order.setOutTradeNo(orderNumber);
order.setTotalFee(MoneyUtil.Yuan2Fen(price));
order.setSpbillCreateIp(IpUtils.getIpAddr(request));
order.setOpenid(openId);
order.setNotifyUrl(WXPayConstants.NOTIFY_URL);
order.setTradeType(WXPayConstants.TRADE_TYPE_JSAPI);
order.setNonceStr(nonceStr);
WXPayUtil.getLogger().info("save 统一下单接口调用,order:{}",order);
// 利用sdk统一下单,已自动调用wxpay.fillRequestData(data);
Map<String, String> response = wxpay.doWxPayApi(order,WXPayConstants.UNIFIEDORDER);
WXPayUtil.getLogger().info("save 下单结果,response:{}",response);
if(response.get(WXPayConstants.RETURN_CODE).equals("SUCCESS")&&response.get(WXPayConstants.RESULT_CODE).equals("SUCCESS")){
String url = request.getQueryString() == null?request.getRequestURL().toString():request.getRequestURL()+"?"+request.getQueryString();
String prepayId = response.get(WXPayConstants.PREPAY_ID);
data = wxpay.permissionValidate(nonceStr,url,prepayId,wxPayConfigExtend.getKey());
return data;
}
} catch (Exception e) {
WXPayUtil.getLogger().error("doUnifiedOrder--下单失败:{}" , e.getMessage());
}
return null;
}
}
wxpay.doWxPayApi(...) инкапсулирует вызов интерфейса заказа:
public Map<String, String> doWxPayApi(WxOrderEntity order,String apiType) {
Map<String, String> resp = null;
try {
Map<String,String> map = new HashMap<>();
map.put("out_trade_no", order.getOutTradeNo());
map.put("nonce_str", order.getNonceStr());
map.put("trade_type", order.getTradeType());
if ("unifiedorder".equalsIgnoreCase(apiType)) {
map.put("spbill_create_ip", order.getSpbillCreateIp());
map.put("openid", order.getOpenid());
map.put("notify_url", order.getNotifyUrl());
map.put("total_fee", String.valueOf(order.getTotalFee()));
map.put("body", order.getBody());
resp = unifiedOrder(map);
} else if ("orderquery".equalsIgnoreCase(apiType)) {
resp = orderQuery(map);
} else if ("closeorder".equalsIgnoreCase(apiType)) {
resp = orderQuery(map);
}
} catch (Exception e) {
WXPayUtil.getLogger().error(order.getOutTradeNo()+" -- 调用接口失败 {}",e.getMessage());
}
return resp;
}
wxPay.doWxPayApi(...) инкапсулирует вторичную проверку подписи:
public Map<String, Object> permissionValidate(String nonceStr, String url, String prepayId, String key) throws Exception {
//jssdk权限验证参数
TreeMap<Object, Object> param = new TreeMap<>();
Map<String, Object> data = new HashMap<>();
param.put("appId", WeChatConstants.APP_ID);
String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
param.put("timestamp", timestamp);//全小写
param.put("nonceStr", nonceStr);
//map.put("signature",WeChatUtil.getSignature(timestamp,uuid,RequestUtil.getUrl(request)));
param.put("signature", WeChatUtil.getSignature(timestamp, nonceStr, url));
data.put("configMap", param);
//微信支付权限验证参数
Map<String, String> payMap = new HashMap<>();
payMap.put("appId", WeChatConstants.APP_ID);
payMap.put("timeStamp", timestamp);//驼峰
payMap.put("nonceStr", nonceStr);
payMap.put("package", "prepay_id=" + prepayId);
payMap.put("signType", "MD5");
payMap.put("paySign", WXPayUtil.generateSignature(payMap, key));
payMap.put("packageStr", "prepay_id=" + prepayId);
data.put("payMap", payMap);
return data;
}
Уведомление о результате платежа и обратный звонок
Настройте контроллер для интерфейса обратного вызова:
@Controller
@RequestMapping("/api/v1/wechat1")
public class NotifyController {
WxBackendServiceImpl wxBackendService;
/**
* 在调用下单接口时,我们会传入 异步接收微信支付结果通知的回调地址,顾名思义这个地址作用就是用来接收支付结果通知,
* 当用户在前端支付成功后,微信服务器会自动调用此地址,然后商户再进行处理
* @param request
* @param response
* @return
*/
@RequestMapping("/wxnotify")
public String wxNotify(HttpServletRequest request, HttpServletResponse response) {
String respXml = "";
try (InputStream in = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
// 获取微信调用我们notify_url的返回信息
String notifyData = new String(baos.toByteArray(), "utf-8");
// 回调处理
respXml = wxBackendService.payCallBack(notifyData);
} catch (Exception e) {
WXPayUtil.getLogger().error("wxnotify:支付回调发布异常:", e.getMessage());
} finally {
try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())){
// 处理业务完毕
bos.write(respXml.getBytes());
} catch (IOException e) {
WXPayUtil.getLogger().error("wxnotify:支付回调发布异常:out:", e.getMessage());
}
}
return respXml;
}
}
Бизнес обратного звонка:
public String payCallBack(String notifyData) throws Exception{
// String respXml = WXPayConstants.RESP_FAIL_XML;
Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
if (WXPayConstants.SUCCESS.equalsIgnoreCase(notifyMap.get(WXPayConstants.RESULT_CODE))) {
WXPayUtil.getLogger().info("payCallBack:微信支付----返回成功");
if (WXPayUtil.isSignatureValid(notifyMap, WXPayConstants.API_KEY)) {
// TODO 数据库操作,付款记录修改 & 记录付款日志
WXPayUtil.getLogger().info("payCallBack:微信支付----验证签名成功,更新数据库");
/*String outTradeNo = notifyMap.get("out_trade_no");
OrderTrading dbOrder = transactionService.findByOutTradeNo(outTradeNo);
// 将未支付状态改为已支付
if (dbOrder != null && dbOrder.getState() == 1) {
// 处理业务 - 修改订单状态
OrderTrading order = new OrderTrading();
order.setOutTradeNo(outTradeNo);
order.setNotifyTime(new Date());
order.setState(1);
transactionService.updateTransOrderByWxnotify(order);
// TODO 数据库更新异常,补偿措施
}*/
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
return WXPayConstants.RESP_SUCCESS_XML;
} else {
WXPayUtil.getLogger().error("payCallBack:微信支付----判断签名错误");
}
} else {
WXPayUtil.getLogger().error("payCallBack:支付失败,错误信息:" + notifyMap.get(WXPayConstants.ERR_CODE_DES));
}
return WXPayConstants.RESP_FAIL_XML;
}
Статические страницы
Страница предзаказа: templates/preOrder.html
Страница подтверждения заказа: templates/toOrder.html
Эта страница используется для проверки подписи и передачи параметров, для удобства наблюдения включен режим отладки.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>立即支付:123</h1>
<button type="submit" id="payBtn">支付</button>
<script th:src="@{/static/js/jquery-1.8.3.min.js}" type="text/javascript" charset="utf-8" rel="stylesheet"></script>
<script type="text/javascript" th:src="@{/static/js/jquery.rotate.min.js}" rel="stylesheet"></script>
<!--微信的JSSDK-->
<script th:src="@{http://res.wx.qq.com/open/js/jweixin-1.2.0.js}"></script>
<script>
$(function() {
<!--通过config接口注入权限验证配置-->
alert('[[${configMap}]]');
alert('[[${payMap}]]');
wx.config({
debug: true, // 开启调试模式
appId: '[[${configMap.appId}]]', // 公众号的唯一标识
timestamp: '[[${configMap.timestamp}]]', // 生成签名的时间戳
nonceStr: '[[${configMap.nonceStr}]]', // 生成签名的随机串
signature: '[[${configMap.signature}]]',// 签名
jsApiList: ['chooseWXPay'] // 填入需要使用的JS接口列表,这里是先声明我们要用到支付的JS接口
});
<!-- config验证成功后会调用ready中的代码 -->
wx.ready(function(){
//点击马上付款按钮
$("#payBtn").click(function(){
//弹出支付窗口
wx.chooseWXPay({
timestamp: '[[${payMap.timeStamp}]]', // 支付签名时间戳,
nonceStr: '[[${payMap.nonceStr}]]', // 支付签名随机串,不长于 32 位
package: '[[${payMap.packageStr}]]', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=xxxx)
signType: '[[${payMap.signType}]]', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '[[${payMap.paySign}]]', // 支付签名
success: function (res) {
// 支付成功后的回调函数
alert("支付成功!");
}
});
})
});
});
</script>
</body>
</html>
Демонстрация эффекта
После запуска проекта нажмите «Подтвердить платеж», чтобы увидеть отображение параметров в режиме отладки. Окончательный эффект оплаты выглядит следующим образом:
Примечание:
- Порт обратного вызова платежа должен быть 80, что должно быть сделано из соображений безопасности.
- Инструмент веб-разработки можно использовать только для отладки, при тестировании платежной функции его необходимо открыть с помощью мобильного телефона.
- Внимательные друзья могут увидеть, что заказ более чем на месяц раньше срока. Это скриншот, который я разработал ранее с учетной записью компании и доменным именем.
Код в этой статье должен показать унифицированный процесс заказа, но он не способствует переносу.В настоящее время код подвергся рефакторингу.
Пожалуйста, смотрите исходный код:GitHub.com/retreat, почему/есть…