Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа

1. Предисловие

Добро пожаловать в этот блог!

Эта статья познакомит вас с новой главой оплаты WeChat в мини-программе Uniapp. Являясь одним из пионеров в области мобильных платежей, WeChat Pay продолжает развиваться и внедрять инновации, чтобы предоставить пользователям и разработчикам более удобный и безопасный способ оплаты. В этой статье мы углубимся в применение и преимущества WeChat Pay в мини-программе Uniapp.

С бурным развитием мобильного Интернета Мини программа стала одной из важных платформ для предоставления пользователями информации и приобретения товаров. Wechat платит как Мини Незаменимый метод оплаты программсередина не только предоставляет пользователям быстрый и удобный метод оплаты, но также создает больше возможностей продаж для продавцов. существует этот фон Вниз, WeChat постоянно контролирует свое существование Мини программа Uniapp Сквозная интеграция для удовлетворения потребностей в платежах в различных сценариях.

В этой статье мы обсудим этапы доступа к WeChat Pay в мини-программе Uniapp и представим различные платежные функции, которые она предоставляет. Мы также проведем углубленное исследование мер безопасности WeChat Pay, чтобы гарантировать сохранность платежной информации пользователей. и средства находятся в безопасности.

Независимо от того, являетесь ли вы разработчиком мини-программ, владельцем бизнеса или читателем, интересующимся технологиями мобильных платежей, эта статья предоставит вам практические знания и советы по интеграции WeChat Pay в мини-программу Uniapp. Давайте вместе исследуем новую главу WeChat Pay и создадим более удобную и безопасную платежную среду для пользователей.

💗 На этот раз речь пойдет о очках знаний по интерфейсу. Если вы не поняли предыдущий абзац, вы можете пойти на склад и скопировать его прямо для использования. Если у вас есть вопросы, вы можете оставить сообщение в области комментариев. Я отвечу вам как можно скорее. Следуйте за мной, чтобы не потеряться. Если эта статья вам поможет, или если у вас есть какие-либо вопросы, оставьте сообщение в области комментариев, я обычно отвечу. увидеть это. Пожалуйста, поставьте лайк и поддержите~ 💗

В этом проекте используется стек технологий

Серверная часть: SpringBoot3.1.x, Mysql8.0, MybatisPlus.

Интерфейс: Vue3, Vite, ElementPlus.

Мини-программы: Uniapp, UviewPlus, Vue3.

Посмотреть демонстрационный адрес: Мини-программа на стороне WEB. См. область комментариев. Здесь нельзя размещать ссылки.

Эта статья содержит около 30 000 слов и напечатана вручную. Надеюсь, она вам поможет. Если вам понравилось, пожалуйста, поставьте лайк и добавьте в избранное. Спасибо.

🌊 Подпишитесь на меня, чтобы не потеряться. Если эта статья вам полезна или у вас есть какие-либо вопросы, оставьте сообщение в области комментариев, я обычно отвечу, когда увижу ее. Пожалуйста, поставьте лайк и поддержите~ 💗

1. Анализ требований к прототипу входа в систему

Вы можете увидеть, что вам нужно войти в систему, как только вы его откроете. Возможно, у студентов есть вопросы🤔️ Почему вы можете напрямую использовать WeChat Pay на ПК, не делая этого?

Если у вас есть сомнения, просто откройте документ и посмотрите, почему.

Платежные документы продавца WeChat: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Вы должны передать openId, чтобы играть и платить, так где я могу получить эту вещь? Вы можете только войти в систему\? ⚠️Примечание: после завершения функции входа на страницу заказа будет добавлена ​​всплывающая функция входа в систему\ ⚠️Примечание: после завершения функции входа на страницу заказа будет добавлена ​​всплывающая функция входа в систему\ ⚠️Примечание: после завершения функции входа в систему на странице заказа будет добавлена ​​всплывающая функция входа в систему.

2. Анализ требований к прототипу страницы заказа

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

Он разделен на три области: верхнюю, среднюю и нижнюю. Код на стороне ПК имеет одинаковый стиль. Мы можем напрямую скопировать файл CSS на стороне ПК, используя метод CV.

Но для тех студентов, которые никогда не видели ПК, я просто выложил файл стиля. См. ниже. глобальный стиль

Введите картинку иллюстрировать
Введите картинку иллюстрировать

3. Глобальный стиль

Сначала мы устанавливаем глобальное дополнение стиля в App.vue.

Установите отступ на 10 резиновых

Язык кода:css
копировать
<style lang="scss">
                  .app-container {
                      padding: 10px !important;
                      box-sizing: border-box !important;
                  }
</style>

Статический стиль файла

существовать static Добавлено в папку css папка\

Добавьте новый глобальный файл пользовательского стиля. global.scss

Язык кода:scss
копировать
#index {
  margin-left: auto;
  margin-right: auto;
  padding: 0 10px 80px 10px;
  box-sizing: border-box;
}

.comm-title {
  overflow: hidden;
  clear: both;
  margin: 40px 0 30px;
}

#footer {
  background-color: #323232;
  border-top-width: 5px;
  border-top-style: solid;
  color: #999;
  width: 100%;
  overflow: hidden;
  padding-top: 30px;
}

.clear {
  clear: both;
  display: block;
  overflow: hidden;
  visibility: hidden;
  width: 0;
  height: 0;
}

#index .content {
  padding: 10px;
  box-sizing: border-box;
  box-shadow: 0 4px 30px #a5a8abcc;
  text-align: center;
  display: flex;
  flex-flow: wrap;
  justify-content: space-between;
}

#index .item {
  margin: 10px;
}

#index .orderBtn {
  position: relative;
  border: 1px solid #f3e2c6;
  background-color: #ffffff;
  color: #ff8686;
  font-weight: bold;
  border-radius: 5px;
  width: 140px;
  height: 50px;
  line-height: 50px;
  font-size: 15px;
  display: inline-block;
  text-align: center;
  text-decoration: none;
}

.current {
  border-color: #ff8686 !important;
}

.current:after {
  content: "";
  display: block;
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 28px;
  height: 28px;
  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAOKADAAQAAAABAAAAOAAAAAANV2hTAAADnUlEQVRoBd3ZzUsUYRwH8O/zzK6llaZkm5mkWdEhN4KEgqKkDhpU9KJpNy/hKch/QK9BdRM7SAQeSnujDgUWXqKgyBBqISKDlswwETXzbZ15eubRGbZ1dp3dmdndmQd255nnZef34Tf7G2UBD7fIta3txKs+FcfA2jwJ1HBq8jwHjMZ5DhiL8xTQCOcZYDycJ4CJcK4HroZzNdAMzrVAszhXApPBuQ6YLM5VwFRwrgGminMF0Aou64FWcVkNtAOXtUC7cFkJtBOXdUC7cVkFdAKXNUCncFkBdBKXcaDTuIwC04HLGDBduIwA04lLOzDduLQCM4FLGzBTuLQAM4lTgZL65lRzGkerWyCduQX41oL9eGvIcOzXJcdxNe2Qqi8LFPv7G4sd+wyBPsNRi4OO4giFVHsdtKpRj1IZ7Nb7sR3bM+gojvohne4E3X1Sd8ivb0Dhr3jNVqCjOH8upLO3QcuPCgtjDEp/G5SBrng2MW4b0BRuTQEwP5kwIMNJvk+60A1aekBMM0WG/LwVLHTfcHn0oC1V1AxOqrsJ6VQnSH4p2NcX0TEk7q8rhu9iL2jJUhFhi/OQn7aAfX6SeN/yrOUiYwanXotsPwxCCEiwiZ9QkQGAJQ4yfxvH9YAUVoh1bGEG8uNmsO+vEu+LmrWUQbM49Xpsbgp0V624NAnsBdlQwjPZFxVKTLdoJ3xND0EKysQEm5uA3NsU93kXs1s/TRmYDE5cbTQEzIyDVh4XpyRQBbI+ADa08nYlgSB8jb18frNYy6ZHsXivHhj9qAdutpMSMGnccjTs1yAwNwm6o0aMkC1BIK8Y7NtLPV5SdhBS/V2Q3I1ijE2EOe48MD6kr0mmkzQwVZwWFBv5wCvpH9CKY2JIFI/cIo7sB6k8AencHZCcPDHHxr4s4aaGte1JH5MCWsVp0bGfA0BkRn+m0ZL9IPxFD10BkXLEMmVkEHJPA7+tx7RtKR1NA+3CaVGy4fcAL/m0/IgYUisl4dVVbUr4DeQHl3imp8S5lTdTQLtxWsBs+B0gR0D5I0RrCq+s8qNmjp/VhiwdVwU6hdOiFv/mRGZBNu2B8qkHyrOrPIURbdryMeGfak7jLEe/ygdw3PTSTW+w0O04lcQIwoZAL+BUIGG0b8Ut6hkc/yb7/P7gfxn0Ck5kj5BO0hoO6UCP4fql8sJWFSqAXsHx75tCCenwlRfWkYbQgsik23Hqo0CtlmpB8fmlLvW2VGFa+wcOncY5YWRXPQAAAABJRU5ErkJggg==) no-repeat;
  background-size: 28px 28px;
}


.PaymentChannel_title {
  position: relative;
  display: flex;
  padding-left: 18 rpx;
  margin: 10px 0;
}

.PaymentChannel_title:before {
  content: "";
  display: block;
  position: absolute;
  left: 0;
  top: calc(50% - 10px);
  width: 4px;
  height: 18px;
  background: #fa8919;
  border-radius: 0 4px 4px 0;
}

.payButtom {
  margin: 30px 0;
  display: flex;
  justify-content: space-around;
}

Добавлен стиль HTML по умолчанию для очистки файлов стилей. reset.scss

Язык кода:scss
копировать
@charset "utf-8";
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;vertical-align:baseline;background:transparent}body{font-size:12px;line-height:160%;font-family:"Helvetica Neue",\5FAE\8F6F\96C5\9ED1,"SimHei",Tohoma;word-break:break-all;word-wrap:break-word;position:relative}ol,ul,li{list-style:none}blockquote,q{quotes:none}table{border-collapse:collapse;border-spacing:0;empty-cells:show}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outline:0}ins,s{text-decoration:none}del{text-decoration:line-through}em,i{font-style:normal}a,img{border:0;text-decoration:none}a{text-decoration:none}a:hover{text-decoration:underline}a:focus{outline:0;-moz-outline:0}a:active{outline:0;blr:expression(this.onFocus=this.blur())}h1{font-size:36px;line-height:45px;font-weight:normal}h2{font-size:24px;line-height:30px;font-weight:normal}h3{font-size:18px;line-height:22px;font-weight:normal}h4{font-size:16px;line-height:20px;font-weight:normal}h5{font-size:14px;line-height:18px;font-weight:normal}h6{font-size:12px;line-height:16px;font-weight:normal}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}

Теперь у нас есть два файла. Если в страницу введут один-два, то хорошо, а если больше, то будет очень хлопотно и некрасиво.

Мы создаем один index.scss Файлы используются для управления Просто импортируйте файл, а затем

Обратите внимание на имя таблицы стилей.

Язык кода:css
копировать
@import 'reset';
@import 'global';

Внедрить глобальное использование

Исправлять main.js Просто поместите Вниз сторону существующего публичного CSS

глобальный стиль
глобальный стиль

4. Построение страницы заказа

Введите картинку иллюстрировать
Введите картинку иллюстрировать

ок, давайте продолжим смотреть, как играть. Вы можете видеть, что вверху есть динамик 🎺, рядом с которым прокручивается текст\.

Зайди в библиотеку компонентов и посмотри

Прокручивающиеся уведомления 📢

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Язык кода:html
копировать
<u-notice-bar color="red" text="В этом случае используетсяJSAPIРежим вытягивания WeChatплатить Всплывающее окно пользователя для WeChatплатитьдействовать"></u-notice-bar>
Введите картинку иллюстрировать
Введите картинку иллюстрировать

Мама моя, это то же самое, что и ElementPlus, так легко радоваться, а об остальном больше не скажу!

Используйте пользовательские стили

Язык кода:html
копировать
<template>
    <view class="app-container">
        <view id="index" class="container">
            я индекс
        </view>
    </view>
</template>
Введите картинку иллюстрировать
Введите картинку иллюстрировать

Следуйте за компьютером и ешьте мясо🥩

Остальное как на ПК, просто вылизываем

⚠️ Все коды Вниз существуют. view id="index" среди

Установить содержимое области заголовка

Язык кода:html
копировать
<u-notice-bar color="red" text="В этом случае используетсяJSAPIРежим вытягивания WeChatплатить Всплывающее окно пользователя для WeChatплатитьдействовать"></u-notice-bar>

<view class="PaymentChannel_title">
    <u-tooltip text="Сайт личного блога: https://yby6.com" copyText="https://yby6.com" overlay></u-tooltip>
</view>
<view class="PaymentChannel_title">
    <u-tooltip text="ПК-система WeChat платит: https://lzys522.cn/wx" copyText="https://lzys522.cn/wx" overlay></u-tooltip>
</view>
<view class="PaymentChannel_title">
    <u-tooltip text="Пример проекта блога: https://lzys522.cn" copyText="https://lzys522.cn" overlay></u-tooltip>
</view>
<view class="PaymentChannel_title"
      style="color: red;  margin-bottom: 10px;font-size: 14px;height: auto !important;">
    <u-tooltip text="Склад с открытым исходным кодом: https://gitee.com/yangbuyi/wxDemo" copyText="https://gitee.com/yangbuyi/wxDemo"
               overlay></u-tooltip>
</view>

настраиватьсерединарегиональный контент<!-- область контента --><view class="content" v-if="productList.length > 0"> <view class="item" v-for="product in productList" :key="product.id"> <a :class="'orderBtn', {current:payOrder.productId === product.id}" @click="selectItem(product.id, (product.price / 100))" href="javascript:void(0);"> {{ product.title }} ¥{{ product.price / 100 }} </a> </view> </view><u-alert description="Уведомление:Платеж Вы можете вернуть деньги после существования списка заказов. Если возврат невозможен, свяжитесь со мной: yangbuyiya». type="warning"></u-alert><view class="btn-arr"> <up-button @click="toPay()" color="#ff959b" :disabled='loading' text="подтверждатьплатитьV3"></up-button> </view>

Полный примеркод
Полный примеркод

взаимодействие

писать коллекция списков

существовать setup средиписатькод(иpcТакой же)

Введите картинку иллюстрировать
Введите картинку иллюстрировать

писать Функциональный метод

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Заказать тест страницы

Обновление инструментов разработки мини-программ.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Моя мама такая красивая~

Небольшая ошибка

Из-за форматирования редактора в rpx появляются пробелы и стиль теряется.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Решение — убрать пробелы

5. Улучшите взаимодействие

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

Остальные интерфейсы API такие же, как на ПК, и их можно скопировать напрямую. Папка API стороннего проекта ПК

Создать запрос API

запрос продукта

Язык кода:javascript
копировать
// axios Отправить ajax-запрос
import request from '@/utils/request';

//Запрос списка продуктов
export function getProductList() {
    return request({
        url: '/api/product/productList',
        method: 'get'
    });
}

Напишите запрос на отправку, чтобы получить список товаров

Язык кода:javascript
копировать
import { getProductList } from "../../api/product";

// Получить список товаров
const selectProductList = async () => {
    const { data } = await getProductList()
    productList.value = data
    payOrder.value.productId = data[0].id
}

Написание жизненного цикла

Мы знаем, что у Vue свой жизненный цикл, и у UniApp тоже есть свой жизненный цикл. Подробную документацию можно найти по адресу: https://uniapp.dcloud.net.cn/collocation/App.html#applifecycle

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Язык кода:javascript
копировать
import { onLoad, onShow } from '@dcloudio/uni-app'

// =========================Жизненный цикл==================== ====
onLoad(() => {
    // Загрузится только один раз
    console.log("onLoad")
    selectProductList()
})
onShow(() => {
    // Будет загружаться каждый раз
})

Наш список не нужно запрашивать повторно. Нам достаточно запросить его только один раз.

Тестовый просмотр

запрос продуктаданные
запрос продуктаданные

Пахнет Уху~

6. Подготовка к авторизации перед оформлением заказа в мини программе

Настройте авторизованный вход в WeChat

модальное окно

<img src="https://foruda.gitee.com/images/1693410126019383931/327436ed\\_5151444.png" alt="Введите картинку иллюстрировать" title="Скриншот" style="zoom:50%;" />

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Напишите переменные для хранения имени пользователя и отображения аватара пользователя.

Язык кода:javascript
копировать
// Авторизация входа
let modal = ref({
    show: false, // Отображать ли
    content: 'Пожалуйста, нажмите на аватар и никнейм, чтобы заполнить информацию. Получить полный сервис WeChat платно!',
    code: '', // Авторизация входаCode
    avatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
    oldAvatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
    nickName: '',// Никнейм
})

писать модальное окно

Введите картинку иллюстрировать
Введите картинку иллюстрировать

⚠️Примечание:Вичатсуществовать Сколько версий не поддерживают авторизацию и возврат имени пользователя?и Аватар может использовать только сам пользовательначальствопроходитьивходить Никнейм

Войдите и авторизируйтесь, чтобы получить аватар

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Язык кода:javascript
копировать
// Авторизация входа - Установить аватар
const onChooseAvatar = (e) => {
    console.log("onChooseAvatar", e);
    const { avatarUrl } = e.detail
    modal.value.avatarUrl = avatarUrl
    uni.setStorageSync('avatarUrl', avatarUrl)
}

Войдите и авторизируйтесь, чтобы получить никнейм

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Язык кода:javascript
копировать
// Авторизация входа - настраивать Никнейм
const changeName = (e) => {
    modal.value.nickName = e.detail.value
}

Подтвердите авторизацию

Использовать жизненный цикл onShow Может запускаться каждый раз при доступе к странице

Язык кода:javascript
копировать
onShow(() => {
    // Сначала проверьте, авторизован ли Получать для входа в систему.
    const storageSync = uni.getStorageSync('token');
    const nickName = uni.getStorageSync('nickName');
    const avatarUrl = uni.getStorageSync('avatarUrl');
    // В противном случае появится всплывающее окно с запросом авторизации для входа в систему.
    if ([ null, undefined, '' ].includes(storageSync)) {
        modal.value.show = true
    } else {
        modal.value.nickName = nickName
        modal.value.avatarUrl = avatarUrl
    }
})

Тестирование получения аватара и ника

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

визуализации
визуализации

Разрешить отправку

Вы можете видеть, что кнопка «Отправить» появляется после того, как мы заполняем аватар и никнейм.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

При анализе требований к прототипу входа мы проанализировали OpenId, необходимый для оформления заказа.

Стиль моей мамы изменился

Введите картинку иллюстрировать
Введите картинку иллюстрировать

ПолучитьOPENID

процесс uniapp ---> Авторизация входа в мини-программу ---> Получить код кода ---> Перейдите на серверную часть, чтобы запросить OpenId в соответствии с кодом кода.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

иллюстрировать

  1. вызов wx.login() Получать Временный код учетных данных для входа и передать его обратно на сервер разработчика.
  2. вызов auth.code2Session интерфейс в обмен на Уникальная идентификация пользователя OpenID 、 пользовательсуществовать Вичат开помещать平台账号ВнизизУникальный идентификаторUnionID(Если текущий Мини программа привязана к учетной записи открытой платформы WeChat) и сеансовый ключ session_key

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

На что следует обратить внимание

  1. сеансовый ключ session_key выполняется на пользовательских данных криптографическая подпись из密钥。为了应用自身изданные安全,Сервер разработчикаСеансовый ключ не должен передаваться мини-программе и не должен передаваться внешнему миру.
  2. Код временного входа в систему можно использовать только один раз.

Разрешить отправку ПолучатьCode

Внизскриншотсредиизкод Все студенты должны бороться самостоятельно

Документ авторизации апплета Uni-app: https://uniapp.dcloud.net.cn/api/plugins/login.html#login

ПолучатьCodeкод
ПолучатьCodeкод

Отправьте запрос на серверную часть, чтобы получить OpenId

Изменить wechatPay.js

Добавьте новый интерфейс запроса. Обратите внимание, что URL-адрес соответствует вашему собственному серверу.

Передаваемые параметры: code、nickName Никнейм в основном используется для того, чтобы отличить пользователя мини-программы, разместившего заказ.

Язык кода:javascript
копировать
// Метод входа ПолучатьopenId
export function loginOrRegister(data) {
    return request({
        url: '/api/wx-pay/js-api/loginOrRegister',
        method: 'post',
        data
    })
}
Введите картинку иллюстрировать
Введите картинку иллюстрировать

Бэкэнд-интерфейс авторизации WeChat

Подробные документы для входа в мини-программу: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

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

Введите картинку иллюстрировать
Введите картинку иллюстрировать

создавать WechatUniAppJsApiController

Не буду вдаваться в подробности про бэкенд, думаю, это всё от больших ребят 😄

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Язык кода:java
копировать
package com.yby6.controller;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yby6.config.WxPayConfig;
import com.yby6.domain.wechat.LoginUser;
import com.yby6.reponse.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * WeChat Мини программаJS API платить
 *
 * @author Yang Shuai
 * Create By 2023/9/9
 */
@Slf4j
@RestController
@RequestMapping("/api/wx-pay/js-api")
@RequiredArgsConstructor
public class WechatUniAppJsApiController {

    private final WxPayConfig wxPayConfig;


    /**
     * WeChat Мини интерфейс входа в программу (Войдите или зарегистрируйтесь)
     *
     * @param loginUser Необходимый code
     */
    @PostMapping("loginOrRegister")
    public R loginOrRegister(@RequestBody LoginUser loginUser) {
        return R.ok(getOpenId(loginUser.getCode()));
    }

    /**
     * Получить уникальный сертификат WeChat
     *
     * @param code код
     * @return {@link String}
     */
    private String getOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        Map<String, Object> map = new HashMap<>();
        map.put("appId", wxPayConfig.getAppid());
        map.put("secret", wxPayConfig.getSecret());
        map.put("js_code", code);
        map.put("grant_type", "authorization_code");
        String post = HttpUtil.post(url, map);
        log.info("WeChat возвращает: {}", post);
        JSONObject obj = JSONUtil.parseObj(post);
        String openid = obj.getStr("openid");
        if (StringUtils.isNoneBlank(openid)) {
            return openid;
        }
        throw new RuntimeException("Временные учетные данные для входаошибка");    }

}

Проверка авторизации апплета

Запустите серверную программу, откройте мини-программу, очистите все кеши и перекомпилируйте.

Появится окно авторизации. После заполнения код будет получен и отправлен на бэкенд для получения openid.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Возвращенный OpenId Мы также сохранили его в локальном кэше среди

openId
openId
Введите картинку иллюстрировать
Введите картинку иллюстрировать

7. Интерфейс заказа мини-программы

Торговая система в первую очередьвызов Долженинтерфейссуществовать Вичатплатить Фон обслуживания генерирует предварительныеплатитьзаказ на транзакцию,После возврата правильного идентификатора сеанса предварительной транзакции сгенерируйте строку транзакции и вызовите оплату в соответствии с различными сценариями, такими как Native, JSAPI и APP.

интерфейсиллюстрировать

Метод запроса:

【POST】/v3/pay/transactions/jsapi


⚠️Примечание: За исключением openId, параметры точно такие же, как на ПК. Если не верите, можете сравнить.

返回изИдентификатор предварительной транзакции используется для Mini Используется, когда программа открывает окно оплаты.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Напишите небольшой интерфейс заказа программы

писать Внизсингл Запросить адрес

существовать enums Папка Внизлапшасоздавать weChatPayJSAPI папкасуществоватьсоздавать WxJSApiType

Язык кода:java
копировать
package com.yby6.enums.weChatPayJSAPI;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * JSAPI Перечисление интерфейсов
 *
 * @author Yang Shuai
 * Create By 2023/09/10
 */
@AllArgsConstructor
@Getter
public enum WxJSApiType {

    /**
     * jsapi Внизсингл
     * POST
     */
    JSAPI_PAY("/v3/pay/transactions/jsapi");


    /**
     * тип
     */
    private final String type;
}

существоватьписать Адрес обратного платежа создавать WxJSNotifyType

Язык кода:java
копировать
package com.yby6.enums.weChatPayJSAPI;

import lombok.Getter;

/**
 * Перечисление обратного вызова JS
 * Обратный звонок получен службой продавца API интерфейс
 */
@Getter
public enum WxJSNotifyType {

    /**
     * платитьуведомить v3
     * /v1/play/callback
     * /api/wx-pay/native/notify
     */
    NATIVE_NOTIFY("/api/wx-pay/js-api/notify"),
    /**
     * Уведомление о результате возврата
     */
    REFUND_NOTIFY("/api/wx-pay/js-api/refunds/notify");

    /**
     * тип
     */
    final String type;

    WxJSNotifyType(String s) {
        this.type = s;
    }

}

⚠️Примечание: Адрес обратного звонка настраивается вами.,Я помещу обратный вызов в интерфейс существования в будущем. WechatUniAppJsApiController Внутри, поэтому адрес интерфейса обратного вызова "/api/wx-pay/js-api/notify"

писать Мини программаединый Внизсинглинтерфейс

Они точно такие же, я их тоже скопировал из интерфейса ПК.

Язык кода:java
копировать
    // Представляем декораторов
		private final CloseableHttpClient wxPayClient;
		// Внедрить услугу заказа
    private final OrderInfoService orderInfoService;
		// Представляем проверку подписи WeChat
    private final Verifier verifier;

    /**
     * Мини программаJSApi вызовединый ВнизсинглAPI,генерироватьплатить二维код
     */
    @SneakyThrows
    @PostMapping("{productId}")
    public R<Map<String, Object>> jsPayPay(@PathVariable Long productId, @RequestParam("openId") String openId) {

        // Воля Никнейм拆出来用В后续我из Заказ展示
        String[] arr = openId.split("\\|");
        String openIdTep = arr[0];
        String nickName = arr.length > 1 ? arr[1] : "Мини программапользователь";        // Создать заказ
        OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId, nickName);
        String prepayId = orderInfo.getCodeUrl(); // prepayId
        if (StrUtil.isNotEmpty(prepayId) && "еще нетплатить".equals(orderInfo.getOrderStatus())) {
            log.info("Заказ сохранен, JSAPI сохранен");
            Map<String, Object> map = WxSignUtil.jsApiCreateSign(prepayId);
            log.info("rouseМини программаплатитьпараметр:{}", map);
            return R.ok(map);
        }


        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxJSApiType.JSAPI_PAY.getType()));
        Map<String, Object> paramsMap = new HashMap<>(14);
        paramsMap.put("appid", wxPayConfig.getAppid());
        paramsMap.put("mchid", wxPayConfig.getMchId());
        paramsMap.put("description", orderInfo.getTitle() + "-" + nickName);
        paramsMap.put("out_trade_no", orderInfo.getOrderNo());
        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxJSNotifyType.NATIVE_NOTIFY.getType()));
        Map<String, Object> amountMap = new HashMap<>();
        amountMap.put("total", orderInfo.getTotalFee());
        amountMap.put("currency", "CNY");
        // Установить сумму
        paramsMap.put("amount", amountMap);
        paramsMap.put("payer", new HashMap<String, Object>() {{
            put("openid", openIdTep);
        }});
        //Преобразуем параметры в строку json
        JSONObject jsonObject = JSONUtil.parseObj(paramsMap);
        log.info("Параметры запроса ===> {0}" + jsonObject);
        StringEntity entity = new StringEntity(jsonObject.toString(), "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        CloseableHttpResponse response = wxPayClient.execute(httpPost);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        JSONObject object = JSONUtil.parseObj(bodyAsString);
        response.close();
        prepayId = object.getStr("prepay_id");
        return R.ok(WxSignUtil.jsApiCreateSign(prepayId));
    }

Код можно оптимизировать. Студенты также могут оптимизировать параметры запроса вручную.

Собрать апплет для вызова параметров платежа

Подробная документация: https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/mini-transfer-payment.html

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Язык кода:java
копировать
// yangbuyi Copyright (c) https://yby6.com 2023.

package com.yby6.wechat;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.yby6.config.WxPayConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * WeChat Мини программа Проверка и расшифровка сообщений
 *
 * @author Yang Shuai
 * Create By 2023/09/10
 * <p>
 */
@Slf4j
@Component
public class WxSignUtil<T> {

    protected static final SecureRandom RANDOM = new SecureRandom();

    /**
     * Сгенерируйте подпись, соберите WeChat и настройте параметры платежа
     * Если вы не понимаете возвращаемые параметры Пожалуйста, посетите официальную документацию WeChat.
     *
     * @param prepayId Вичат Внизсингл返回изprepay_id
     * @return Параметры, необходимые для текущего вызова платежа
     */
    public static Map<String, Object> jsApiCreateSign(String prepayId) {
        if (StringUtils.isNotBlank(prepayId)) {
            final WxPayConfig wxPayConfig = SpringUtil.getBean(WxPayConfig.class);
            final String appid = wxPayConfig.getAppid();
            // Загрузить подпись
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonceStr = String.valueOf(System.currentTimeMillis());
            String packageStr = "prepay_id=" + prepayId;
            String packageSign = sign(buildMessage(appid, timeStamp, nonceStr, packageStr).getBytes(StandardCharsets.UTF_8), wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath()));
            Map<String, Object> packageParams = new HashMap<>(6);
            packageParams.put("appId", appid);
            packageParams.put("timeStamp", timeStamp);
            packageParams.put("nonceStr", nonceStr);
            packageParams.put("package", packageStr);
            packageParams.put("signType", "RSA");
            packageParams.put("paySign", packageSign);
            return packageParams;
        }
        return null;
    }

    /**
     * Создать подпись
     * <p>
     * Мини программаappId
     * Временная метка
     * случайная строка
     * Расширенная строка сведений о заказе
     */
    public static String sign(byte[] message, PrivateKey privateKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey); // Загрузить закрытый ключ продавца
            sign.update(message); // UTF-8
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Текущая среда Java не поддерживает SHA256withRSA", e);
        } catch (SignatureException e) {
            throw new RuntimeException("Вычисление подписи не удалось", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("Неверный закрытый ключ", e);
        }
    }

    /**
     * генерироватьслучайная строка Базовые методы WeChat
     */
    protected static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(RANDOM.nextInt("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".length()));
        }
        return new String(nonceChars);
    }

    /**
     * Сортировка в соответствии со спецификациями документа подписи внешнего интерфейса, \n — это новая строка.
     *
     * @param appId     appId
     * @param timestamp время
     * @param nonceStr  случайная строка
     * @param prepayIds prepay_id
     */
    public static String buildMessage(String appId, String timestamp, String nonceStr, String prepayIds) {
        return appId + "\n" + timestamp + "\n" + nonceStr + "\n" + prepayIds + "\n";
    }


    /**
     * Расшифровать симметрия Расшифровать
     * ссылка: <a href="https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient/blob/master/src/main/java/com/wechat/pay/contrib/apache/httpclient/util/AesUtil.java">...</a>
     *
     * @param plainText Хибун
     * @return {@link String}
     */
    public static <T> T decryptFromResource(String plainText, Class<T> clazz) {
        Map<String, Object> bodyMap = JSONUtil.toBean(plainText, Map.class);
        log.info("Личный текст Расшифровать");
        final WxPayConfig wxPayConfig = SpringUtil.getBean(WxPayConfig.class);
        //Получены данные уведомления resource узел
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        // зашифрованный текст данных
        String ciphertext = resourceMap.get("ciphertext");
        //случайная строка
        String nonce = resourceMap.get("nonce");
        //Дополнительные данные
        String associatedData = resourceMap.get("associated_data");
        log.info("Личный текст ===> {}", ciphertext);
        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        // использовать ключ、nonceиassociated_data,对данные密文resource.ciphertextруководить Расшифровать,получатьJSON形式из资源对象
        String resource;
        try {
            resource = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
        log.info("обычный текст ===> {}", resource);
        return JSONUtil.toBean(resource, clazz);
    }


}

Сравнить параметры запроса сборки

Слушай, я тебе не вру. Давайте просто скопируем этот новый раздел. payer openid поле

Параметры запроса
Параметры запроса

Мини-программа обратного звонка по оплате заказа

Излишне говорить, что это то же самое, что и ПК.

Язык кода:java
копировать
    // Введение в процессинг Платеж журнал успеха
    private final WxJSAPIPayService wxJSAPIPayService;


/**
 * платитьуведомить->Вичатплатитьпроходитьплатитьуведомитьинтерфейс Воляпользователь Платеж уведомление об успешном сообщении торговцам
 *
 * @return {@link R}
 */
@PostMapping("/notify")
public Map<String, String> transactionCallBack(HttpServletRequest request, HttpServletResponse response) {
    Map<String, String> map = new HashMap<>(12);
    try {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");

        log.info("timestamp:" + timestamp + " nonce:" + nonce + " serialNo:" + serialNo + " signature:" + signature);
        //Обработка параметров уведомления
        String body = HttpUtils.readData(request);
        log.info("платить пароль уведомления: {} ", body);
        JSONObject jsonObject = JSONUtil.parseObj(body);
        final String requestId = jsonObject.getStr("id");

        //Проверка подписи
        WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(verifier, requestId, body);
        if (!wechatPay2ValidatorForRequest.validate(request)) {
            log.error("Проверка уведомления не удалась");
            //неудачный ответ
            response.setStatus(500);
            return WechatRep.fail();
        }
        // Обработать заказ
        final CallBackResource decrypt = WxSignUtil.decryptFromResource(body, CallBackResource.class);
        wxJSAPIPayService.processOrder(JSONUtil.toJsonStr(decrypt));
        log.info("Обработка обратного вызова завершена");
        response.setHeader("Content-type", ContentType.JSON.toString());
        response.getOutputStream().write(JSONUtil.toJsonStr(WechatRep.ok()).getBytes(StandardCharsets.UTF_8));
        response.flushBuffer();
    } catch (Exception e) {
        log.error("Не удалось обработать обратный вызов WeChat:", e);
    }
    // успешный ответ
    response.setStatus(200);
    return WechatRep.ok();
}

Напишите небольшую программу для записи журналов заказов.

существовать service средисоздавать WxJSAPIPayService , если честно, хахаха, я тоже прям с ПК получил.

Язык кода:java
копировать
package com.yby6.service;

import cn.hutool.json.JSONUtil;
import com.yby6.config.WxPayConfig;
import com.yby6.domain.OrderInfo;
import com.yby6.domain.wechat.CallBackResource;
import com.yby6.enums.OrderStatus;
import com.yby6.enums.weChatPayNative.WxNotifyType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Yang Shuai
 * Create By 2023/9/9
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class WxJSAPIPayService {

    private final ReentrantLock lock = new ReentrantLock();
    private final OrderInfoService orderInfoService;
    private final PaymentInfoService paymentInfoService;

    /**
     * обратный вызов jsapi
     */
    public void processOrder(String plainText) {
        final CallBackResource data = JSONUtil.toBean(plainText, CallBackResource.class);

        log.info("Обработать заказ");
        // Специальное напоминание WeChat:
        // Проводить проверку статуса бизнес-данных перед обработкой.
        // Блокировки данных следует использовать для управления параллелизмом, чтобы избежать хаоса данных, вызванного повторным входом функции.

        // Попробуйте Получить блокировки:
        // Если это удастся, он немедленно вернет true, если потерпит неудачу, он немедленно вернет false. Нет необходимости ждать снятия блокировки.
        if (lock.tryLock()) {
            try {
                // Обработка дубликатов уведомлений
                // интерфейсвызовиз幂等性:несмотря ни на чтоинтерфейсодеяловызовсколько раз,产生из结果是一致из。
                OrderInfo orderInfo = orderInfoService.lambdaQuery().eq(OrderInfo::getOrderNo, (data.getOutTradeNo())).one();

                if (null != orderInfo && !OrderStatus.NOTPAY.getType().equals(orderInfo.getOrderStatus())) {
                    log.info("Дубликат уведомления, уже Платеж успешный Ла");
                    return;
                }

                // Имитировать параллелизм уведомлений
                //TimeUnit.SECONDS.sleep(5);

                // Обновить статус заказа
                orderInfoService.lambdaUpdate().eq(OrderInfo::getOrderNo, data.getOutTradeNo()).set(OrderInfo::getOrderStatus, OrderStatus.SUCCESS.getType()).update();
                log.info("Обновить статус заказа,Номер заказа: {},Статус заказа: {}", data.getOutTradeNo(), OrderStatus.SUCCESS);
                // Запись в журнал
                paymentInfoService.createPaymentInfo(plainText);
            } finally {
                // Чтобы активно снять блокировку
                lock.unlock();
            }
        }
    }
    /*==========================================================================*/
}

Сопоставление классов сущностей для обратного вызова платежа

Язык кода:java
копировать
// yangbuyi Copyright (c) https://yby6.com 2023.

package com.yby6.domain.wechat;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * js API платить回调 *
 * @author Yang Shuai
 * Create By 2023/9/9
 */
@NoArgsConstructor
@Data
public class CallBackResource {

    /**
     * mchid
     */
    @JsonProperty("mchid")
    private String mchid;
    /**
     * appid
     */
    @JsonProperty("appid")
    private String appid;
    /**
     * сделка нет
     */
    @JsonProperty("out_trade_no")
    private String outTradeNo;
    /**
     * Идентификатор заказа транзакции
     */
    @JsonProperty("transaction_id")
    private String transactionId;
    /**
     * Тип сделки
     */
    @JsonProperty("trade_type")
    private String tradeType;
    /**
     * торговая страна
     */
    @JsonProperty("trade_state")
    private String tradeState;
    /**
     * торговая странаdesc
     */
    @JsonProperty("trade_state_desc")
    private String tradeStateDesc;
    /**
     * Тип банка
     */
    @JsonProperty("bank_type")
    private String bankType;
    /**
     * дополнительный
     */
    @JsonProperty("attach")
    private String attach;
    /**
     * 成功время
     */
    @JsonProperty("success_time")
    private String successTime;
    /**
     * Плательщик
     */
    @JsonProperty("payer")
    private PayerDTO payer;
    /**
     * количество
     */
    @JsonProperty("amount")
    private AmountDTO amount;

    /**
     * ../
     *
     * @author Yang Shuai
     * Create By 2023/05/24
     */
    @NoArgsConstructor
    @Data
    public static class PayerDTO {
        /**
         * openid
         */
        @JsonProperty("openid")
        private String openid;
    }

    /**
     * ../
     *
     * @author Yang Shuai
     * Create By 2023/05/24
     */
    @NoArgsConstructor
    @Data
    public static class AmountDTO {
        /**
         * общий
         */
        @JsonProperty("total")
        private Integer total;
        /**
         * Плательщикобщий
         */
        @JsonProperty("payer_total")
        private Integer payerTotal;
        /**
         * валюта
         */
        @JsonProperty("currency")
        private String currency;
        /**
         * платитьвалюта
         */
        @JsonProperty("payer_currency")
        private String payerCurrency;
    }
}

На этот раз новый код

新增изкод
新增изкод

Протестируйте единый интерфейс заказа мини-программы

Перезапустите мини-программу, перезапустите серверную службу, включите проникновение в интрасеть, авторизуйте вход в мини-программу и получите копию openId.

openId: o6Yr-xxxxxxxxxx

openid
openid

Измените идею серверной части и используйте инструмент отладки интерфейса для отправки запроса на заказ.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Идеально без проблем

Введите картинку иллюстрировать
Введите картинку иллюстрировать

8. Мини программа открывает окно оплаты.

uni.requestPayment(OBJECT)

платить

uni.requestPayment — это клиентский платёжный API, который объединяет различные платформы. Будь то определенная Мини-программа или существующее приложение, клиент использует этот APIвызовплатить.

Когда этот API работает на каждом конце, он автоматически преобразуется в собственный API платвызов на каждом конце.

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

Например, в WeChat есть приложения платно и Мини. Различные порталы приложений, такие как «программировать» и «H5платить», используют процесс, соответствующий uni-app. Что касается существующих приложений, вам необходимо подать заявку на получение приложения WeChat бесплатно и Мини. программаподать заявку на WeChat’s Mini программаплатить。

Подробный адрес документа: https://uniapp.dcloud.net.cn/api/plugins/payment.html#%E7%94%B3%E8%AF%B7%E6%B5%81%E7%A8%8B-2

Введите картинку иллюстрировать
Введите картинку иллюстрировать

Эти параметры такие же, как мы собрали. Дальше нам остаётся только задать параметры и запустить вызовплатить.

Напишите небольшую программу для унификации запросов заказов.

Изменить wechatPay.js

Язык кода:javascript
копировать
// единыйJSAPIВнизсингл
export function JSAPI(productId, openId) {
    return request({
        'url': `/api/wx-pay/js-api/${productId}`,
        'method': 'post',
		'params': {
			"openId" : openId
		}
    })
}			

Улучшить функцию toPay

Язык кода:javascript
копировать
// инициироватьплатить
const toPay = async () => {
  // Получать Вичатплатитьсертификатсоздаватьплатить Заказ  const storageSync = uni.getStorageSync('token');
  const nickName = uni.getStorageSync('nickName');

  // Отправить Мини программаединый Внизсингл
  const {code, data} = await JSAPI(payOrder.value.productId, storageSync + "|" + nickName)

  if (code !== 200) {
    toast("Ошибка создания заказа, повторите попытку позже!")
    return
  }
  toast("Создавать Заказ успешно, подождите...")
  setTimeout(() => {
    const wx = data
    // вызов Вичатплатить弹窗    uni.requestPayment({
      provide: 'wxpay',
      timeStamp: wx.timeStamp, // 当前время
      nonceStr: wx.nonceStr, // случайная строка
      package: wx.package, // prepayId
      signType: wx.signType, // Алгоритм подписи
      paySign: wx.paySign, // платитьзнак      success: (res) => {
        loading.value = false
        toast("Платеж успехшен Пожалуйста, существует список заказов, чтобы проверить статус заказа, а функция возврата также существует в списке заказов середина", 5)
      },
      fail: (res) => {
        console.log(res);
        toast("Отменить оплату, вы можете продолжать нажимать кнопку оплаты, чтобы начать заново")
        loading.value = false
      }
    })
  }, 500)

}

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

Запустите мини-программу, запустите серверную службу, начните проникновение во внутреннюю сеть арахисовой оболочки, очистите кеш мини-программы.

Введите картинку иллюстрировать
Введите картинку иллюстрировать

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

Платеж успешен

Введите картинку иллюстрировать
Введите картинку иллюстрировать
Введите картинку иллюстрировать
Введите картинку иллюстрировать
Введите картинку иллюстрировать
Введите картинку иллюстрировать

наконец

Этот вопрос исчерпан. Увидимся в следующий раз👋~

🌊 Подпишитесь на меня, чтобы не потеряться. Если эта статья вам полезна или у вас есть какие-либо вопросы, оставьте сообщение в области комментариев, я обычно отвечу, когда увижу ее. Пожалуйста, поставьте лайк и поддержите~ 💗

ЯсуществоватьучаствоватьНа втором этапе специального тренировочного лагеря Tencent Technology Creation 2023 года будут отмечены призовые эссе, которые разделят призовой фонд в 10 000 юаней и часы с клавиатурой.

Введите картинку иллюстрировать
Введите картинку иллюстрировать
boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода