From eba51880c54a062bc36717c56ecc8ef081dc77bf Mon Sep 17 00:00:00 2001 From: yansongda Date: Mon, 22 Aug 2022 15:22:34 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BD=BF=E7=94=A8=20vitepress=20&&=20?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E8=87=B3=E4=B8=BB=E5=BA=93=20=20(#650)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 3 +- LICENSE | 4 +- web/.gitignore | 3 + web/.vitepress/config.ts | 29 + web/.vitepress/nav.ts | 26 + web/.vitepress/sidebar.ts | 15 + .../theme/components/HomeAuthorize.vue | 79 ++ .../theme/components/HomePrimary.vue | 61 ++ web/.vitepress/theme/index.ts | 14 + web/.vitepress/versions.ts | 1 + web/docs/v1/index.md | 967 ++++++++++++++++++ web/docs/v1/sidebar.js | 1 + web/docs/v2/alipay/callback.md | 25 + web/docs/v2/alipay/cancel.md | 25 + web/docs/v2/alipay/close.md | 27 + web/docs/v2/alipay/download.md | 27 + web/docs/v2/alipay/find.md | 48 + web/docs/v2/alipay/index.md | 90 ++ web/docs/v2/alipay/pay.md | 191 ++++ web/docs/v2/alipay/refund.md | 26 + web/docs/v2/alipay/response.md | 23 + web/docs/v2/contribute.md | 13 + web/docs/v2/events/class.md | 74 ++ web/docs/v2/events/index.md | 9 + web/docs/v2/events/usage.md | 32 + web/docs/v2/index.md | 123 +++ web/docs/v2/installation.md | 13 + web/docs/v2/logger/usage.md | 26 + web/docs/v2/others/faq.md | 29 + web/docs/v2/quickUsage.md | 149 +++ web/docs/v2/sidebar.js | 62 ++ web/docs/v2/wechat/callback.md | 38 + web/docs/v2/wechat/cancel.md | 7 + web/docs/v2/wechat/close.md | 31 + web/docs/v2/wechat/find.md | 68 ++ web/docs/v2/wechat/index.md | 109 ++ web/docs/v2/wechat/pay.md | 249 +++++ web/docs/v2/wechat/refund.md | 34 + web/docs/v2/wechat/response.md | 23 + web/docs/v3/alipay/callback.md | 42 + web/docs/v3/alipay/cancel.md | 22 + web/docs/v3/alipay/close.md | 19 + web/docs/v3/alipay/download.md | 27 + web/docs/v3/alipay/find.md | 54 + web/docs/v3/alipay/more.md | 137 +++ web/docs/v3/alipay/pay.md | 208 ++++ web/docs/v3/alipay/refund.md | 20 + web/docs/v3/alipay/response.md | 19 + web/docs/v3/index.md | 45 + web/docs/v3/kernel/pipeline.md | 4 + web/docs/v3/kernel/plugin.md | 364 +++++++ web/docs/v3/kernel/rocket.md | 90 ++ web/docs/v3/kernel/shortcut.md | 53 + web/docs/v3/others/event.md | 91 ++ web/docs/v3/others/faq.md | 37 + web/docs/v3/others/logger.md | 46 + web/docs/v3/overview/business.md | 7 + web/docs/v3/overview/communication.md | 23 + web/docs/v3/overview/contribute.md | 7 + web/docs/v3/overview/donate.md | 43 + web/docs/v3/overview/versions.md | 3 + web/docs/v3/quick-start/alipay.md | 141 +++ web/docs/v3/quick-start/init.md | 150 +++ web/docs/v3/quick-start/install.md | 104 ++ web/docs/v3/quick-start/return-format.md | 62 ++ web/docs/v3/quick-start/wechat.md | 132 +++ web/docs/v3/sidebar.js | 79 ++ web/docs/v3/upgrade/v3.0.md | 28 + web/docs/v3/upgrade/v3.1.md | 25 + web/docs/v3/wechat/callback.md | 40 + web/docs/v3/wechat/cancel.md | 9 + web/docs/v3/wechat/close.md | 24 + web/docs/v3/wechat/find.md | 61 ++ web/docs/v3/wechat/more.md | 244 +++++ web/docs/v3/wechat/pay.md | 249 +++++ web/docs/v3/wechat/refund.md | 28 + web/docs/v3/wechat/response.md | 19 + web/index.md | 18 + web/package.json | 13 + web/public/images/icon.png | Bin 0 -> 21145 bytes web/public/images/jetbrains.png | Bin 0 -> 2687 bytes web/public/images/logo.png | Bin 0 -> 9426 bytes web/public/images/logo2.png | Bin 0 -> 21145 bytes web/public/images/pay.jpg | Bin 0 -> 72144 bytes web/yarn.lock | 727 +++++++++++++ 85 files changed, 6285 insertions(+), 3 deletions(-) create mode 100644 web/.gitignore create mode 100644 web/.vitepress/config.ts create mode 100644 web/.vitepress/nav.ts create mode 100644 web/.vitepress/sidebar.ts create mode 100644 web/.vitepress/theme/components/HomeAuthorize.vue create mode 100644 web/.vitepress/theme/components/HomePrimary.vue create mode 100644 web/.vitepress/theme/index.ts create mode 100644 web/.vitepress/versions.ts create mode 100644 web/docs/v1/index.md create mode 100644 web/docs/v1/sidebar.js create mode 100644 web/docs/v2/alipay/callback.md create mode 100644 web/docs/v2/alipay/cancel.md create mode 100644 web/docs/v2/alipay/close.md create mode 100644 web/docs/v2/alipay/download.md create mode 100644 web/docs/v2/alipay/find.md create mode 100644 web/docs/v2/alipay/index.md create mode 100644 web/docs/v2/alipay/pay.md create mode 100644 web/docs/v2/alipay/refund.md create mode 100644 web/docs/v2/alipay/response.md create mode 100644 web/docs/v2/contribute.md create mode 100644 web/docs/v2/events/class.md create mode 100644 web/docs/v2/events/index.md create mode 100644 web/docs/v2/events/usage.md create mode 100644 web/docs/v2/index.md create mode 100644 web/docs/v2/installation.md create mode 100644 web/docs/v2/logger/usage.md create mode 100644 web/docs/v2/others/faq.md create mode 100644 web/docs/v2/quickUsage.md create mode 100644 web/docs/v2/sidebar.js create mode 100644 web/docs/v2/wechat/callback.md create mode 100644 web/docs/v2/wechat/cancel.md create mode 100644 web/docs/v2/wechat/close.md create mode 100644 web/docs/v2/wechat/find.md create mode 100644 web/docs/v2/wechat/index.md create mode 100644 web/docs/v2/wechat/pay.md create mode 100644 web/docs/v2/wechat/refund.md create mode 100644 web/docs/v2/wechat/response.md create mode 100644 web/docs/v3/alipay/callback.md create mode 100644 web/docs/v3/alipay/cancel.md create mode 100644 web/docs/v3/alipay/close.md create mode 100644 web/docs/v3/alipay/download.md create mode 100644 web/docs/v3/alipay/find.md create mode 100644 web/docs/v3/alipay/more.md create mode 100644 web/docs/v3/alipay/pay.md create mode 100644 web/docs/v3/alipay/refund.md create mode 100644 web/docs/v3/alipay/response.md create mode 100644 web/docs/v3/index.md create mode 100644 web/docs/v3/kernel/pipeline.md create mode 100644 web/docs/v3/kernel/plugin.md create mode 100644 web/docs/v3/kernel/rocket.md create mode 100644 web/docs/v3/kernel/shortcut.md create mode 100644 web/docs/v3/others/event.md create mode 100644 web/docs/v3/others/faq.md create mode 100644 web/docs/v3/others/logger.md create mode 100644 web/docs/v3/overview/business.md create mode 100644 web/docs/v3/overview/communication.md create mode 100644 web/docs/v3/overview/contribute.md create mode 100644 web/docs/v3/overview/donate.md create mode 100644 web/docs/v3/overview/versions.md create mode 100644 web/docs/v3/quick-start/alipay.md create mode 100644 web/docs/v3/quick-start/init.md create mode 100644 web/docs/v3/quick-start/install.md create mode 100644 web/docs/v3/quick-start/return-format.md create mode 100644 web/docs/v3/quick-start/wechat.md create mode 100644 web/docs/v3/sidebar.js create mode 100644 web/docs/v3/upgrade/v3.0.md create mode 100644 web/docs/v3/upgrade/v3.1.md create mode 100644 web/docs/v3/wechat/callback.md create mode 100644 web/docs/v3/wechat/cancel.md create mode 100644 web/docs/v3/wechat/close.md create mode 100644 web/docs/v3/wechat/find.md create mode 100644 web/docs/v3/wechat/more.md create mode 100644 web/docs/v3/wechat/pay.md create mode 100644 web/docs/v3/wechat/refund.md create mode 100644 web/docs/v3/wechat/response.md create mode 100644 web/index.md create mode 100644 web/package.json create mode 100644 web/public/images/icon.png create mode 100644 web/public/images/jetbrains.png create mode 100644 web/public/images/logo.png create mode 100644 web/public/images/logo2.png create mode 100644 web/public/images/pay.jpg create mode 100644 web/yarn.lock diff --git a/.gitattributes b/.gitattributes index 0b3245f..b2f4278 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ /tests export-ignore /.github export-ignore -/.phpunit.cache +/.phpunit.cache export-ignore +/docs export-ignore .gitattributes export-ignore .gitignore export-ignore phpunit.php export-ignore diff --git a/LICENSE b/LICENSE index 501400d..c6dbf1f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) yansongda +Copyright (c) 2017 yansongda Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..79989ef --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.vitepress/dist/ +yarn-error.log diff --git a/web/.vitepress/config.ts b/web/.vitepress/config.ts new file mode 100644 index 0000000..b679dd4 --- /dev/null +++ b/web/.vitepress/config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from 'vitepress'; +import nav from './nav'; +import sidebar from "./sidebar"; + +export default defineConfig({ + lang: 'zh-CN', + title: 'Pay', + description: '让支付开发更简单', + lastUpdated: true, + head: [ + ['link', { rel: 'icon', href: '/images/icon.png' }] + ], + themeConfig: { + logo: '/images/logo2.png', + nav: nav, + sidebar: sidebar, + socialLinks: [ + { icon: 'github', link: 'https://github.com/yansongda/pay' }, + ], + editLink: { + pattern: 'https://github.com/yansongda/pay/edit/master/web/:path', + text: 'Edit this page on GitHub' + }, + footer: { + message: 'Released under the MIT License.', + copyright: 'Copyright © 2017-present yansongda' + } + } +}) diff --git a/web/.vitepress/nav.ts b/web/.vitepress/nav.ts new file mode 100644 index 0000000..7bc7299 --- /dev/null +++ b/web/.vitepress/nav.ts @@ -0,0 +1,26 @@ +export default [ + { text: 'Home', link: '/' }, + { + text: '版本', + items: [ + { + text: '停止维护', + items: [ + { text: 'v1.x', link: '/docs/v1/', activeMatch: '^/docs/v1/' } + ] + }, + { + text: '安全支持', + items: [ + { text: 'v2.x', link: '/docs/v2/', activeMatch: '^/docs/v2/' } + ] + }, + { + text: '积极开发中', + items: [ + { text: 'v3.x', link: '/docs/v3/', activeMatch: '^/docs/v3/' } + ] + } + ] + } +] diff --git a/web/.vitepress/sidebar.ts b/web/.vitepress/sidebar.ts new file mode 100644 index 0000000..0ef60e0 --- /dev/null +++ b/web/.vitepress/sidebar.ts @@ -0,0 +1,15 @@ +// @ts-ignore +import path from 'path' +import versions from "./versions"; + +let sidebars = versions.reduce( + (sidebars, version) => ({ + ...sidebars, + [`/docs/${version}/`]: require(path.join( + __dirname, `../docs/${version}/sidebar` + )) + }), + {} +); + +export default sidebars; diff --git a/web/.vitepress/theme/components/HomeAuthorize.vue b/web/.vitepress/theme/components/HomeAuthorize.vue new file mode 100644 index 0000000..7f77826 --- /dev/null +++ b/web/.vitepress/theme/components/HomeAuthorize.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/web/.vitepress/theme/components/HomePrimary.vue b/web/.vitepress/theme/components/HomePrimary.vue new file mode 100644 index 0000000..7a673a6 --- /dev/null +++ b/web/.vitepress/theme/components/HomePrimary.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/web/.vitepress/theme/index.ts b/web/.vitepress/theme/index.ts new file mode 100644 index 0000000..fd90acb --- /dev/null +++ b/web/.vitepress/theme/index.ts @@ -0,0 +1,14 @@ +import { h } from 'vue' +import DefaultTheme from 'vitepress/theme' +import HomePrimary from './components/HomePrimary.vue' +import HomeAuthorize from './components/HomeAuthorize.vue' + +export default { + ...DefaultTheme, + Layout() { + return h(DefaultTheme.Layout, null, { + 'home-hero-before': () => h(HomePrimary), + 'home-features-after': () => h(HomeAuthorize) + }) + } +} diff --git a/web/.vitepress/versions.ts b/web/.vitepress/versions.ts new file mode 100644 index 0000000..e7de6f0 --- /dev/null +++ b/web/.vitepress/versions.ts @@ -0,0 +1 @@ +export default ['v3', 'v2', 'v1']; diff --git a/web/docs/v1/index.md b/web/docs/v1/index.md new file mode 100644 index 0000000..f2baf72 --- /dev/null +++ b/web/docs/v1/index.md @@ -0,0 +1,967 @@ +# v1 版本 + +**!注意:v1.x 版本已 EOF!请大家尽快迁移到 v3 版本!** + +开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。 + +**说明,请先熟悉支付宝说明文档!!** + +欢迎 Star,欢迎 PR! + +laravel 扩展包请 [https://github.com/yansongda/laravel-pay](https://github.com/yansongda/laravel-pay) + +## 特点 +- 命名不那么乱七八糟 +- 隐藏开发者不需要关注的细节 +- 根据支付宝、微信最新 API 开发而成 +- 高度抽象的类,免去各种拼json与xml的痛苦 +- 符合 PSR 标准,你可以各种方便的与你的框架集成 +- 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关 +- 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的 + + +## 运行环境 +- PHP 5.6+ +- composer + + +## 支持的支付网关 + +由于各支付网关参差不齐,所以我们抽象了两个方法 `driver()`,`gateway()`。 + +两个方法的作用如下: + +`driver()` : 确定支付平台,如 `alipay`,`wechat`; + +`gateway()`: 确定支付网关。通过此方法,确定支付平台下的支付网关。例如,支付宝下有 「电脑网站支付」,「手机网站支付」,「APP 支付」三种支付网关,通过传入 `web`,`wap`,`app` 确定。 + +详细思路可以查看源代码。 + +### 1、支付宝 + +- 电脑支付 +- 手机网站支付 +- APP 支付 +- 刷卡支付 +- 扫码支付 + +SDK 中对应的 driver 和 gateway 如下表所示: + +| driver | gateway | 描述 | +| :----: | :-----: | :-------: | +| alipay | web | 电脑支付 | +| alipay | wap | 手机网站支付 | +| alipay | app | APP 支付 | +| alipay | pos | 刷卡支付 | +| alipay | scan | 扫码支付 | +| alipay | transfer | 帐户转账(可用于平台用户提现) | + +### 2、微信 + +- 公众号支付 +- 小程序支付 +- H5 支付 +- 扫码支付 +- 刷卡支付 +- APP 支付 + +SDK 中对应的 driver 和 gateway 如下表所示: + +| driver | gateway | 描述 | +| :----: | :-----: | :-------: | +| wechat | mp | 公众号支付 | +| wechat | miniapp | 小程序支付 | +| wechat | wap | H5 支付 | +| wechat | scan | 扫码支付 | +| wechat | pos | 刷卡支付 | +| wechat | app | APP 支付 | +| wechat | transfer | 企业付款 | + +## 支持的方法 + +所有网关均支持以下方法 + +- pay(array $config_biz) +说明:支付接口 +参数:数组类型,订单业务配置项,包含 订单号,订单金额等 +返回:mixed 详情请看「支付网关配置说明与返回值」一节。 + +- refund(array|string $config_biz, $refund_amount = null) +说明:退款接口 +参数:`$config_biz` 为字符串类型仅对`支付宝支付`有效,此时代表订单号,第二个参数为退款金额。 +返回:mixed 退款成功,返回 服务器返回的数组;否则返回 false; + +- close(array|string $config_biz) +说明:关闭订单接口 +参数:`$config_biz` 为字符串类型时代表订单号,如果为数组,则为关闭订单业务配置项,配置项内容请参考各个支付网关官方文档。 +返回:mixed 关闭订单成功,返回 服务器返回的数组;否则返回 false; + +- find(string $out_trade_no) +说明:查找订单接口 +参数:`$out_trade_no` 为订单号。 +返回:mixed 查找订单成功,返回 服务器返回的数组;否则返回 false; + +- verify($data, $sign = null) +说明:验证服务器返回消息是否合法 +参数:`$data` 为服务器接收到的原始内容,`$sign` 为签名信息,当其为空时,系统将自动转化 `$data` 为数组,然后取 `$data['sign']`。 +返回:mixed 验证成功,返回 服务器返回的数组;否则返回 false; + + +## 安装 +```shell +composer require "yansongda/pay:^1.0" +``` + +## 使用说明 + +### 0、一个完整的例子: +```php + [ + 'app_id' => '2016082000295641', + 'notify_url' => 'http://yansongda.cn/alipay_notify.php', + 'return_url' => 'http://yansongda.cn/return.php', + 'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB', + 'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==', + ], + ]; + + public function index() + { + $config_biz = [ + 'out_trade_no' => time(), + 'total_amount' => '1', + 'subject' => 'test subject', + ]; + + $pay = new Pay($this->config); + + return $pay->driver('alipay')->gateway()->pay($config_biz); + } + + public function return(Request $request) + { + $pay = new Pay($this->config); + + return $pay->driver('alipay')->gateway()->verify($request->all()); + } + + public function notify(Request $request) + { + $pay = new Pay($this->config); + + if ($pay->driver('alipay')->gateway()->verify($request->all())) { + // 请自行对 trade_status 进行判断及其它逻辑进行判断,在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。 + // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号; + // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额); + // 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email); + // 4、验证app_id是否为该商户本身。 + // 5、其它业务逻辑情况 + file_put_contents(storage_path('notify.txt'), "收到来自支付宝的异步通知\r\n", FILE_APPEND); + file_put_contents(storage_path('notify.txt'), '订单号:' . $request->out_trade_no . "\r\n", FILE_APPEND); + file_put_contents(storage_path('notify.txt'), '订单金额:' . $request->total_amount . "\r\n\r\n", FILE_APPEND); + } else { + file_put_contents(storage_path('notify.txt'), "收到异步通知\r\n", FILE_APPEND); + } + + echo "success"; + } +} + +``` +```php + [ + 'app_id' => 'wxb3f6xxxxxxxxxx', + 'mch_id' => '1457xxxxx2', + 'notify_url' => 'http://yansongda.cn/wechat_notify.php', + 'key' => 'mF2suE9sU6Mk1Cxxxxxxxxxx45', + 'cert_client' => './apiclient_cert.pem', + 'cert_key' => './apiclient_key.pem', + ], + ]; + + public function index() + { + $config_biz = [ + 'out_trade_no' => 'e2', + 'total_fee' => '1', // **单位:分** + 'body' => 'test body', + 'spbill_create_ip' => '8.8.8.8', + 'openid' => 'onkVf1FjWS5SBIihS-123456_abc', + ]; + + $pay = new Pay($this->config); + + return $pay->driver('wechat')->gateway('mp')->pay($config_biz); + } + + public function notify(Request $request) + { + $pay = new Pay($this->config); + $verify = $pay->driver('wechat')->gateway('mp')->verify($request->getContent()); + + if ($verify) { + file_put_contents('notify.txt', "收到来自微信的异步通知\r\n", FILE_APPEND); + file_put_contents('notify.txt', '订单号:' . $verify['out_trade_no'] . "\r\n", FILE_APPEND); + file_put_contents('notify.txt', '订单金额:' . $verify['total_fee'] . "\r\n\r\n", FILE_APPEND); + } else { + file_put_contents(storage_path('notify.txt'), "收到异步通知\r\n", FILE_APPEND); + } + + echo "success"; + } +} + +``` + +### 1、准备配置参数 + +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_trade_no' => '12', // 订单号 + 'total_amount' => '13', // 订单金额,单位:元,**微信支付,单位:分** + 'subject' => 'test subject', // 订单商品标题 +]; +``` + +### 2、在代码中使用 + +```php +driver('alipay')->gateway('web')->pay($config_biz); +``` + + +## 错误 + +使用非跳转接口(如, `refund` 接口,`close` 接口)时,如果在调用相关支付网关 API 时有错误产生,会抛出 `GatewayException` 错误,可以通过 `$e->getMessage()` 查看,同时,也可通过 `$e->raw` 查看调用 API 后返回的原始数据,该值为数组格式。 + + +## 支付网关配置说明与返回值 + +由于支付网关不同,每家参数参差不齐,为了方便,我们抽象定义了两个参数:`$config`,`$config_biz`,分别为全局参数,业务参数。但是,所有配置参数均为官方标准参数,无任何差别。 + +「业务参数」为订单相关的参数,「全局参数」为除订单相关参数以外的全局性参数。 + +具体参数列表请查看每个支付网关的使用说明。 + +### 1、支付宝 - 电脑网站支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_trade_no' => '12', // 订单号 + 'total_amount' => '13', // 订单金额,单位:元 + 'subject' => 'test subject', // 订单商品标题 +]; +``` + +#### 所有配置参数 + +所有参数均为官方标准参数,无任何差别。[点击这里](https://docs.open.alipay.com/common/105901 '支付宝官方文档') 查看官方文档。 + +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + 'return_url' => '', // 同步通知 url,*强烈建议加上本参数* + 'notify_url' => '', // 异步通知 url,*强烈建议加上本参数* + ], +]; +$config_biz = [ + 'out_trade_no' => '', + 'total_amount' => '', + 'subject' => '', + + // 订单描述 + 'body' => '', + + // 订单包含的商品列表信息,Json格式: {"show_url":"https://或http://打头的商品的展示地址"} ,在支付时,可点击商品名称跳转到该地址 + 'goods_detail' => '', + + // 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。该参数在请求到支付宝时开始计时。 + 'timeout_express' => '', + + // 禁用渠道,用户不可用指定渠道支付当有多个渠道时用“,”分隔注:与enable_pay_channels互斥 + 'disable_pay_channels' => '', + + // 可用渠道,用户只能在指定渠道范围内支付当有多个渠道时用“,”分隔注:与disable_pay_channels互斥 + 'enable_pay_channels' => '', + + // 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。支付宝只会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝 + 'passback_params' => '', + + // 业务扩展参数,详见 [业务扩展参数说明](https://docs.open.alipay.com/#kzcs) + 'extend_params' => '', + + // 商品主类型:0—虚拟类商品,1—实物类商品(默认)注:虚拟类商品不支持使用花呗渠道 + 'goods_type' => '', + + // 获取用户授权信息,可实现如免登功能。获取方法请查阅:用户信息授权 + 'auth_token' => '', + + /** + * PC扫码支付的方式,支持前置模式和跳转模式。 + * + * 前置模式是将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以iframe方式请求支付宝页面。具体分为以下几种: + * 0:订单码-简约前置模式,对应iframe宽度不能小于600px,高度不能小于300px; + * 1:订单码-前置模式,对应iframe宽度不能小于300px,高度不能小于600px; + * 3:订单码-迷你前置模式,对应iframe宽度不能小于75px,高度不能小于75px; + * 4:订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小。 + * + * 跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。 + * 2:订单码-跳转模式 + */ + 'qr_pay_mode' => '', + + // 商户自定义二维码宽度 注:qr_pay_mode=4时该参数生效 + 'qrcode_width' => '' +]; +``` + +#### 返回值 +- pay() +类型:string +说明:该接口返回跳转到支付宝支付的 Html 代码。 + +### 2、支付宝 - 手机网站支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_trade_no' => '12', // 订单号 + 'total_amount' => '13', // 订单金额,单位:元 + 'subject' => 'test subject', // 订单商品标题 +]; +``` + +#### 所有配置参数 + +该网关大部分参数和 「电脑支付」 相同,具体请参考 [官方文档](https://docs.open.alipay.com/203/107090/ '支付宝手机网站支付文档') + +#### 返回值 +- pay() +类型:string +说明:该接口返回跳转到支付宝支付的 Html 代码。 + +### 3、支付宝 - APP 支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'notify_url' => '', // 支付宝异步通知地址 + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_trade_no' => '12', // 订单号 + 'total_amount' => '13', // 订单金额,单位:元 + 'subject' => 'test subject', // 订单商品标题 +]; +``` + +#### 所有配置参数 +该网关大部分参数和 「电脑支付」 相同,具体请参考 [官方文档](https://docs.open.alipay.com/204/105465/ '支付宝APP支付文档') + +#### 返回值 +- pay() +类型:string +说明:该接口返回用于客户端调用的 orderString 字符串,可直接供 APP 客户端调用,客户端调用方法不在此文档讨论范围内,[Android 用户请看这里](https://docs.open.alipay.com/204/105300/),[Ios 用户请看这里](https://docs.open.alipay.com/204/105299/)。 + +### 4、支付宝 - 刷卡支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_trade_no' => '12', // 订单号 + 'total_amount' => '13', // 订单金额,单位:元 + 'subject' => 'test subject', // 订单商品标题 + 'auth_code' => '123456', // 授权码 +]; +``` + +#### 所有配置参数 +该网关大部分参数和 「电脑支付」 相同,具体请参考 [官方文档](https://docs.open.alipay.com/api_1/alipay.trade.pay ' 支付宝APP支付文档') + +#### 返回值 +- pay() +类型:array|bool +说明:该接口成功时返回服务器响应的数组;验签失败返回 false。 + +### 5、支付宝 - 扫码支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'notify_url' => '', // 支付宝异步通知地址 + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_trade_no' => '12', // 订单号 + 'total_amount' => '13', // 订单金额,单位:元 + 'subject' => 'test subject', // 订单商品标题 +]; +``` + +#### 所有配置参数 +该网关大部分参数和 「电脑支付」 相同,具体请参考 [官方文档](https://docs.open.alipay.com/api_1/alipay.trade.precreate ' 支付宝APP支付文档') + +#### 返回值 +- pay() +类型:string +说明:该接口返回二维码链接,可以通过其他库转换为二维码供用户扫描。 + +### 6、支付宝 - 帐户转账 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_biz_no' => '', // 订单号 + 'payee_type' => 'ALIPAY_LOGONID', // 收款方账户类型(ALIPAY_LOGONID | ALIPAY_USERID) + 'payee_account' => 'demo@sandbox.com', // 收款方账户 + 'amount' => '10', // 转账金额 +]; +``` + +#### 所有配置参数 +```php + [ + 'app_id' => '', // 支付宝提供的 APP_ID + 'ali_public_key' => '', // 支付宝公钥,1行填写 + 'private_key' => '', // 自己的私钥,1行填写 + ], +]; +$config_biz = [ + 'out_biz_no' => '', // 订单号 + 'payee_type' => 'ALIPAY_LOGONID', // 收款方账户类型(ALIPAY_LOGONID | ALIPAY_USERID) + 'payee_account' => 'demo@sandbox.com', // 收款方账户 + 'amount' => '10', // 转账金额 + 'payer_show_name' => '未寒', // 付款方姓名 + 'payee_real_name' => '张三', // 收款方真实姓名 + 'remark' => '张三', // 转账备注 +]; +``` + + [官方文档](https://doc.open.alipay.com/docs/api.htm?apiId=1321&docType=4 ' 单笔转账到支付宝账户接口') + + +#### 返回值 +- pay() +类型:array|bool +说明:该接口成功时返回服务器响应的数组;验签失败返回 false。 + +### 7、微信 - 公众号支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 公众号APPID + 'mch_id' => '', // 微信商户号 + 'notify_url' => '', + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 支付人的 IP + 'openid' => '', // 支付人的 openID +]; +``` + +#### 所有配置参数 +所有参数均为官方标准参数,无任何差别。[点击这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 '微信支付官方文档') 查看官方文档。 +```php + [ + 'endpoint_url' => 'https://apihk.mch.weixin.qq.com/', // optional, default 'https://api.mch.weixin.qq.com/' + 'app_id' => '', // 公众号APPID + 'mch_id' => '', // 微信商户号 + 'notify_url' => '', + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 支付人的 IP + 'openid' => '', // 支付人的 openID + + // 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB" + 'device_info' => '', + + // 商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明” + 'detail' => '', + + // 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 + 'attach' => '', + + // 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型 + 'fee_type' => '', + + // 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 + 'time_start' => '', + + // 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则注意:最短失效时间间隔必须大于5分钟 + 'time_expire' => '', + + // 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠 + 'goods_tag' => '', + + // trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。 + 'product_id' => '', + + // 上传此参数no_credit--可限制用户不能使用信用卡支付 + 'limit_pay' => '', + + // 该字段用于上报场景信息,目前支持上报实际门店信息。该字段为JSON对象数据,对象格式为{"store_info":{"id": "门店ID","name": "名称","area_code": "编码","address": "地址" }} ,字段详细说明请点击行前的+展开 + 'scene_info' => '', +]; +``` + +#### 返回值 +- pay() +类型:array +说明:返回用于 微信内H5调起支付 的所需参数数组。后续调用不在本文档讨论范围内,具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6)。 + +后续调用举例: + +```html + +``` + +### 8、微信 - 小程序支付 + +#### 最小配置参数 +```php + [ + 'miniapp_id' => '', // 小程序APPID + 'mch_id' => '', // 微信商户号 + 'notify_url' => '', + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 支付人的 IP + 'openid' => '', // 支付人的 openID +]; +``` + +#### 所有配置参数 +由于「小程序支付」和「公众号支付」都使用的是 JSAPI,所以,除了 APPID 一个使用的是公众号的 APPID 一个使用的是 小程序的 APPID 以外,该网关所有参数和 「公众号支付」 相同,具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1)。 + +#### 返回值 +- pay() +类型:array +说明:返回用于 小程序调起支付API 的所需参数数组。后续调用不在本文档讨论范围内,具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3)。 + +### 9、微信 - H5 支付 +#### 最小配置参数 + +```php + [ + 'app_id' => '', // 微信公众号 APPID + 'mch_id' => '', // 微信商户号 + 'return_url' => '', // *此配置选项可选*,注意,该跳转 URL 只有跳转之意,没有同步通知功能 + 'notify_url' => '', + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 支付人的 IP +]; +``` + +#### 所有配置参数 +所有配置项和前面支付网关相差不大,请[点击这里查看](https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1). + +#### 返回值 +- pay() +类型:string +说明:返回微信支付中间页网址,可直接 302 跳转。 + +### 10、微信 - 扫码支付 +这里使用「模式二」进行扫码支付,具体请[参考这里](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5) + +#### 最小配置参数 + +```php + [ + 'app_id' => '', // 微信公众号 APPID + 'mch_id' => '', // 微信商户号 + 'notify_url' => '', + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 调用 API 服务器的 IP + 'product_id' => '', // 订单商品 ID +]; +``` + +#### 所有配置参数 +所有配置项和前面支付网关相差不大,请[点击这里查看](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1) + +#### 返回值 +- pay() +类型:string +说明:返回微信支付二维码 URL 地址,可直接将此 url 生成二维码,展示给用户进行扫码支付。 + +### 11、微信 - 刷卡支付 + +#### 最小配置参数 +```php + [ + 'app_id' => '', // 公众号 APPID + 'mch_id' => '', // 微信商户号 + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 支付人的 IP + 'auth_code' => '', // 授权码 +]; +``` + +#### 所有配置参数 +该网关所有参数和其它支付网关基本相同,具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1)。 + +#### 返回值 +- pay() +类型:array +说明:返回用于服务器返回的数组。返回参数请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1)。 + +### 12、微信 - APP 支付 + +#### 最小配置参数 +```php + [ + 'appid' => '', // APPID + 'mch_id' => '', // 微信商户号 + 'notify_url' => '', + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'out_trade_no' => '', // 订单号 + 'total_fee' => '', // 订单金额,**单位:分** + 'body' => '', // 订单描述 + 'spbill_create_ip' => '', // 支付人的 IP +]; +``` + +#### 所有配置参数 +该网关所有参数和其它支付网关相同相同,具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)。 + +#### 返回值 +- pay() +类型:array +说明:返回用于 小程序调起支付API 的所需参数数组。后续调用不在本文档讨论范围内,具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5)。 + +### 12、微信 - 企业付款 + +#### 最小配置参数 +```php + [ + 'appid' => '', // APPID + 'mch_id' => '', // 微信商户号 + 'key' => '', // 微信支付签名秘钥 + 'cert_client' => './apiclient_cert.pem', // 客户端证书路径,退款时需要用到 + 'cert_key' => './apiclient_key.pem', // 客户端秘钥路径,退款时需要用到 + ], +]; + +$config_biz = [ + 'partner_trade_no' => '', //商户订单号 + 'openid' => '', //收款人的openid + 'check_name' => 'NO_CHECK', //NO_CHECK:不校验真实姓名\FORCE_CHECK:强校验真实姓名 +// 're_user_name'=>'张三', //check_name为 FORCE_CHECK 校验实名的时候必须提交 + 'amount' => 100, //企业付款金额,单位为分 + 'desc' => '帐户提现', //付款说明 + 'spbill_create_ip' => '192.168.0.1', //发起交易的IP地址 +]; +``` + +#### 所有配置参数 +具体请看 [官方文档](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2)。 + +#### 返回值 +- pay() +类型:array +说明:返回用于 支付结果 的数组。具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2)。 + +### 13、微信 - 发放裂变红包 + +#### 最小配置参数 +```php + [ + 'app_id'=>'wxaxxxxxxxx', + 'mch_id' => '1442222202', + 'key' => 'ddddddddddddddd', + 'cert_client' => 'D:\php\xxx\application\wxpay\cert\apiclient_cert.pem', + 'cert_key' => 'D:\php\xxx\application\wxpay\cert\apiclient_key.pem', + ], + ]; + + $config_biz = [ + 'wxappid'=>'wxaxxxxxxxx', + 'mch_billno' => 'hb'.time(), + 'send_name'=>'萌点云科技',//商户名称 + 're_openid'=>'ogg5JwsssssssssssCdXeD_S54',//用户openid + 'total_amount' =>333, // 付款金额,单位分 + 'wishing'=>'提前祝你狗年大吉',//红包祝福语 + 'client_ip'=>'192.168.0.1',//调用接口的机器Ip地址 + 'total_num'=>'3',//红包发放总人数 + 'act_name'=>'提前拜年',//活动名称 + 'remark'=>'提前祝你狗年大吉,苟富贵勿相忘!', //备注 + 'amt_type'=>'ALL_RAND',//ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额 + ]; + + $pay = new Pay($config); + try + { + $res= $pay->driver('wechat')->gateway('groupredpack')->pay($config_biz); + + }catch (Exception $e){ + + } + +``` + +#### 所有配置参数 +具体请看 [官方文档](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4)。 + +#### 返回值 +- pay() +类型:array +说明:返回用于 支付结果 的数组。具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4)。 + + +### 14、微信 - 发放普通红包 + +#### 最小配置参数 +```php + [ + 'app_id'=>'wxaxxxxxxxx', + 'mch_id' => '1442222202', + 'key' => 'ddddddddddddddd', + 'cert_client' => 'D:\php\xxx\application\wxpay\cert\apiclient_cert.pem', + 'cert_key' => 'D:\php\xxx\application\wxpay\cert\apiclient_key.pem', + ], + ]; + + $config_biz = [ + 'wxappid'=>'wxaxxxxxxxx', + 'mch_billno' => 'hb'.time(), + 'send_name'=>'萌点云科技',//商户名称 + 're_openid'=>'ogg5JwsssssssssssCdXeD_S54',//用户openid + 'total_amount' =>100, // 付款金额,单位分 + 'wishing'=>'提前祝你狗年大吉',//红包祝福语 + 'client_ip'=>'192.168.0.1',//调用接口的机器Ip地址 + 'total_num'=>'1',//红包发放总人数 + 'act_name'=>'提前拜年',//活动名称 + 'remark'=>'提前祝你狗年大吉,苟富贵勿相忘!', //备注 + ]; + + $pay = new Pay($config); + try + { + $res= $pay->driver('wechat')->gateway('redpack')->pay($config_biz); + + }catch (Exception $e){ + + } + +``` + +#### 所有配置参数 +具体请看 [官方文档](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3)。 + +#### 返回值 +- pay() +类型:array +说明:返回用于 支付结果 的数组。具体请 [参考这里](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3)。 +## 代码贡献 +由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 + +如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_** + +## LICENSE +MIT diff --git a/web/docs/v1/sidebar.js b/web/docs/v1/sidebar.js new file mode 100644 index 0000000..66c7e24 --- /dev/null +++ b/web/docs/v1/sidebar.js @@ -0,0 +1 @@ +exports = module.exports = [] diff --git a/web/docs/v2/alipay/callback.md b/web/docs/v2/alipay/callback.md new file mode 100644 index 0000000..3d57597 --- /dev/null +++ b/web/docs/v2/alipay/callback.md @@ -0,0 +1,25 @@ +# 接收回调 + +| 方法名 | 参数 | 返回值 | +|:------:|:---:|:----------:| +| verify | 无 | Collection | + +使用的加密方式为支付宝官方推荐的 **RSA2**,目前只支持这一种加密方式,且没有支持其他加密方式的计划。 + +## 例子 + +```php +$result = $alipay->verify(); +// 是的,你没有看错,就是这么简单! + +// return $alipay->success()->send(); // laravel 框架直接 return $alipay->success(); +``` + +## 配置参数 + +无 + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/alipay/cancel.md b/web/docs/v2/alipay/cancel.md new file mode 100644 index 0000000..ca0c165 --- /dev/null +++ b/web/docs/v2/alipay/cancel.md @@ -0,0 +1,25 @@ +# 取消 + +| 方法名 | 参数 | 返回值 | +|:------:|:-------------------:|:----------:| +| cancel | string/array $order | Collection | + +## 例子 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = $alipay->cancel($order); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.cancel/),查看「请求参数」一栏。 + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/alipay/close.md b/web/docs/v2/alipay/close.md new file mode 100644 index 0000000..0eb302b --- /dev/null +++ b/web/docs/v2/alipay/close.md @@ -0,0 +1,27 @@ +# 关闭 + +| 方法名 | 参数 | 返回值 | +|:-----:|:-------------------:|:----------:| +| close | string/array $order | Collection | + +## 例子 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; + +// $order = '1514027114'; + +$result = $alipay->close($order); +``` + + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.close/),查看「请求参数」一栏。 + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/alipay/download.md b/web/docs/v2/alipay/download.md new file mode 100644 index 0000000..da3b88d --- /dev/null +++ b/web/docs/v2/alipay/download.md @@ -0,0 +1,27 @@ +# 下载对账单 + +| 方法名 | 参数 | 返回值 | +|:--------:|:------------------:|:------:| +| download | string/array $bill | string | + +## 例子 + +```php +$bill = [ + 'bill_date' => '2016-04-05', // 2016-04 + 'bill_type' => 'trade' +]; + +// $bill = '2016-04-05'; + +$url = $alipay->download($bill); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_15/alipay.data.dataservice.bill.downloadurl.query),查看「请求参数」一栏。 + + +## 返回值 + +返回 string 类型。直接返回账单下载链接。 diff --git a/web/docs/v2/alipay/find.md b/web/docs/v2/alipay/find.md new file mode 100644 index 0000000..3022e6f --- /dev/null +++ b/web/docs/v2/alipay/find.md @@ -0,0 +1,48 @@ +# 查询 + +| 方法名 | 参数 | 返回值 | +|:----:|:--------------------------------------:|:----------:| +| find | string/array $order, string/null $type | Collection | + +## 查询普通支付订单 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; + +// $order = '1514027114'; + +$result = $alipay->find($order); +``` + +## 查询退款订单 + +```php +$order = [ + 'out_trade_no' => '1514027114', + 'out_request_no' => '1514027114' +]; + +$result = $alipay->find($order, 'refund'); +``` + + +## 查询转账订单 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = $alipay->find($order, 'transfer'); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.query/),查看「请求参数」一栏。 + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/alipay/index.md b/web/docs/v2/alipay/index.md new file mode 100644 index 0000000..03cfcff --- /dev/null +++ b/web/docs/v2/alipay/index.md @@ -0,0 +1,90 @@ +# 概述 + +**请先熟悉 支付宝支付 开发文档!** + +使用的加密方式为支付宝官方推荐的 **RSA2**,目前只支持这一种加密方式,且没有支持其他加密方式的计划。 + +## 快速上手 + +```php +use Yansongda\Pay\Pay; + +$config = [ + 'app_id' => '2016082000295641', + 'notify_url' => 'http://yansongda.cn/notify.php', + 'return_url' => 'http://yansongda.cn/return.php', + 'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB', + 'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==', + 'log' => [ // optional + 'file' => './logs/alipay.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + // 'mode' => 'dev', // optional,设置此参数,将进入沙箱模式 +]; + +// 支付 +$order = [ + 'out_trade_no' => time(), + 'total_amount' => '1', + 'subject' => 'test subject - 测试', +]; + +$alipay = Pay::alipay($config)->web($order); + +return $alipay->send();// laravel 框架中请直接 `return $alipay` + +// 退款 +$order = [ + 'out_trade_no' => '1514027114', + 'refund_amount' => '0.01', +]; + +$result = Pay::alipay($config)->refund($order); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + + +// 查询 +$result = Pay::alipay($config)->find('out_trade_no_123456'); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + + +// 取消 +$result = Pay::alipay($config)->cancel('out_trade_no_123456'); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + + +// 关闭 +$result = Pay::alipay($config)->close('out_trade_no_123456'); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + + +// 验证服务器数据 +$alipay = Pay::alipay($config) + +// 是的,验签就这么简单! +$data = $alipay->verify(); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$data->xxx` 访问服务器返回的数据。 + +$alipay->success()->send(); // 向支付宝服务器确认接收到的数据。laravel 框架中请直接 `return $alipay->success()` +``` + +## 服务商模式 + +在原有 config 配置基础上mode 设为 `service`新增 pid 参数。具体如下: + +```php +$config=[ + //省略上述参数 + ... + 'mode' => 'service',//设为service + 'pid' => '2016082000295641',//这里填写支付服务商的pid信息 +]; +``` + + +## 注意 + +后续文档中,如果没有特别说明, `$alipay` 均代表`Pay::alipay($config)` + diff --git a/web/docs/v2/alipay/pay.md b/web/docs/v2/alipay/pay.md new file mode 100644 index 0000000..ff233b8 --- /dev/null +++ b/web/docs/v2/alipay/pay.md @@ -0,0 +1,191 @@ +# 支付 + +支付宝支付目前支持 7 种支付方法,对应的支付 method 如下: + +| method | 说明 | 参数 | 返回值 | +|:--------:|:------:|:------------:|:----------:| +| web | 电脑支付 | array $order | Response | +| wap | 手机网站支付 | array $order | Response | +| app | APP 支付 | array $order | Response | +| pos | 刷卡支付 | array $order | Collection | +| scan | 扫码支付 | array $order | Collection | +| transfer | 账户转账 | array $order | Collection | +| mini | 小程序支付 | array $order | Collection | + +## 电脑支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'test subject-测试订单', + // 'http_method' => 'GET' // 如果想在 wap 支付时使用 GET 方式提交,请加上此参数。默认使用 POST 方式提交 +]; + +return $alipay->web($order)->send(); // laravel 框架中请直接 return $alipay->web($order) +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/270/alipay.trade.page.pay),查看「请求参数」一栏。 + + +## 手机网站支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'test subject-测试订单', + // 'http_method' => 'GET' // 如果想在 wap 支付时使用 GET 方式提交,请加上此参数。默认使用 POST 方式提交 +]; + +return $alipay->wap($order)->send(); // laravel 框架中请直接 return $alipay->wap($order) +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/203/107090/),查看「请求参数」一栏。 + + +## APP 支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'test subject-测试订单', +]; + +// 将返回字符串,供后续 APP 调用,调用方式不在本文档讨论范围内,请参考官方文档。 +return $alipay->app($order)->send(); // laravel 框架中请直接 return $alipay->app($order) +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/204/105465/),查看「请求参数」一栏。 + + +## 刷卡支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'test subject-刷卡支付', + 'auth_code' => '289756915257123456', +]; + +$result = $alipay->pos($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.pay),查看「请求参数」一栏。 + + +## 扫码支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'test subject-刷卡支付', +]; + +$result = $alipay->scan($order); +//二维码内容: $qr = $result->qr_code; +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.precreate),查看「请求参数」一栏。 + + +## 账户转账 + +### 例子 + +```php +$order = [ + 'out_biz_no' => time(), + 'trans_amount' => '0.01', + 'product_code' => 'TRANS_ACCOUNT_NO_PWD', + 'payee_info' => [ + 'identity' => 'ghdhjw7124@sandbox.com', + 'identity_type' => 'ALIPAY_LOGON_ID', + ], +]; + +$result = $alipay->transfer($order); +``` + +### 查询转账订单 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = $alipay->find($order, 'transfer'); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了** + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_28/alipay.fund.trans.uni.transfer/),查看「请求参数」一栏。 + + +## 小程序支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'subject' => 'test subject-小程序支付', + 'total_amount' => '0.01', + 'buyer_id' => 2088622190161234, +]; + +$result = $alipay->mini($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.create/),查看「请求参数」一栏。 + +小程序支付接入文档:[https://docs.alipay.com/mini/introduce/pay](https://docs.alipay.com/mini/introduce/pay)。 + +## 返回值 + +**各支付方法返回值请顶部表格** + +返回只会返回两种类型 `Symfony\Component\HttpFoundation\Response` 或 `Yansongda\Supports\Collection` + +* 返回 Response 类型时,可以通过 `return $response->send()` 直接进行返回(laravel 框架中使用请直接`return $response` ) +* 返回 Collection 类型时,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/alipay/refund.md b/web/docs/v2/alipay/refund.md new file mode 100644 index 0000000..2b941b6 --- /dev/null +++ b/web/docs/v2/alipay/refund.md @@ -0,0 +1,26 @@ +# 退款 + +| 方法名 | 参数 | 返回值 | +|:------:|:------------:|:----------:| +| refund | array $order | Collection | + +## 退款操作 + +```php +$order = [ + 'out_trade_no' => '1514027114', + 'refund_amount' => '0.01', +]; + +$result = $alipay->refund($order); +``` + + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_1/alipay.trade.refund),查看「请求参数」一栏。 + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/alipay/response.md b/web/docs/v2/alipay/response.md new file mode 100644 index 0000000..77d5595 --- /dev/null +++ b/web/docs/v2/alipay/response.md @@ -0,0 +1,23 @@ +# 确认回调 + +| 方法名 | 参数 | 返回值 | +|:-------:|:---:|:--------:| +| success | 无 | Response | + +## 例子 + +```php +// $result = $alipay->verify(); + +return $alipay->success()->send(); // laravel 框架直接 return $alipay->success(); +``` + + +## 订单配置参数 + +无 + + +## 返回值 + +返回 Response 类型,可以通过`return $response->send();` 进行返回;如果在 laravel 框架中,可直接 `return $response;` diff --git a/web/docs/v2/contribute.md b/web/docs/v2/contribute.md new file mode 100644 index 0000000..c37b653 --- /dev/null +++ b/web/docs/v2/contribute.md @@ -0,0 +1,13 @@ +# 代码贡献 + +由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 + +如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_** + +## 赏一杯瑞幸吧 + +![pay](/images/pay.jpg) + +## LICENSE + +MIT diff --git a/web/docs/v2/events/class.md b/web/docs/v2/events/class.md new file mode 100644 index 0000000..71fd2e1 --- /dev/null +++ b/web/docs/v2/events/class.md @@ -0,0 +1,74 @@ +# 事件说明 + +## 支付开始 + +- 事件类:Yansongda\Pay\Events\PayStarting::class +- 说明:此事件将在最开始进行支付时进行抛出。此时 SDK 只进行了相关初始化操作,其它所有操作均未开始。 +- 额外数据: + - $driver (支付机构) + - $gateway (支付网关) + - $params (传递的原始参数) + + +## 支付初始化完毕 + +- 事件类:Yansongda\Pay\Events\PayStarted +- 说明:此事件将在所有参数处理完毕时抛出。 +- 额外数据: + - $driver (支付机构) + - $gateway (支付网关) + - $endpoint (支付的 url endpoint) + - $payload (数据) + + +## 开始调用API + +- 事件类:Yansongda\Pay\Events\ApiRequesting +- 说明:此事件将在请求支付方的 API 前抛出。 +- 额外数据: + - $driver (支付机构) + - $gateway (支付网关) + - $endpoint (支付的 url endpoint) + - $payload (数据) + + +## 调用API结束 + +- 事件类:Yansongda\Pay\Events\ApiRequested +- 说明:此事件将在请求支付方的 API 完成之后抛出。 +- 额外数据: + - $driver (支付机构) + - $gateway (支付网关) + - $endpoint (支付的 url endpoint) + - $result (请求后的返回数据) + + +## 验签失败 + +- 事件类:Yansongda\Pay\Events\SignFailed +- 说明:此事件将在签名验证失败时抛出。 +- 额外数据: + - $driver (支付机构) + - $gateway (支付网关) + - $data (验签数据) + + +## 收到通知 + +- 事件类:Yansongda\Pay\Events\RequestReceived +- 说明:此事件将在收到支付方的请求(通常在异步通知或同步通知)时抛出 +- 额外数据: + - $driver (支付机构) + - $gateway (支付网关) + - $data (收到的数据) + + +## 调用其它方法 + +- 事件类:Yansongda\Pay\Events\MethodCalled +- 说明:此事件将在调用除 PAYMETHOD 方法(例如,查询订单,退款,取消订单)时抛出 +- 额外数据: + - $driver (支付机构) + - $gateway (调用方法) + - $endpoint (支付的 url endpoint) + - $payload (数据) diff --git a/web/docs/v2/events/index.md b/web/docs/v2/events/index.md new file mode 100644 index 0000000..58e39ff --- /dev/null +++ b/web/docs/v2/events/index.md @@ -0,0 +1,9 @@ +# 概述 + +:::tip +v2.6.0-beta.1 及以上可用 +::: + +在支付过程中,可能会想监听一些事件,好同时处理一些其它任务。 + +SDK 使用 [symfony/event-dispatcher](https://github.com/symfony/event-dispatcher) 组件进行事件的相关操作。 diff --git a/web/docs/v2/events/usage.md b/web/docs/v2/events/usage.md new file mode 100644 index 0000000..bb0ce38 --- /dev/null +++ b/web/docs/v2/events/usage.md @@ -0,0 +1,32 @@ +# 事件使用 + +::: tip +使用事件系统前,确保已初始化 pay。即调用了 `Pay::xxx($config)` +::: + +```php +driver // alipay/wechat + // 支付 gateway:$event->gateway // app/web/pos/scan ... + // 支付传递的参数:$event->params + + // coding to send email... + } +} + +// 2. 添加监听器 +Events::addListener(PayStarting::class, [new PayStartingListener(), 'sendEmail']); + +// 3. 喝杯咖啡 + +``` diff --git a/web/docs/v2/index.md b/web/docs/v2/index.md new file mode 100644 index 0000000..1516318 --- /dev/null +++ b/web/docs/v2/index.md @@ -0,0 +1,123 @@ +# 快速入门 + +该文档为 v2.x 版本,如果您想找 v1.x 版本文档,请点击[https://github.com/yansongda/pay/tree/v1](https://github.com/yansongda/pay/tree/v1) + +**注意:v1.x 与 v2.x 版本不兼容** + +开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。 + +**!!请先熟悉 支付宝/微信 说明文档!!请具有基本的 debug 能力!!** + +欢迎 Star,欢迎 PR! + +laravel 扩展包请 [https://github.com/yansongda/laravel-pay](https://github.com/yansongda/laravel-pay) + +QQ交流群:690027516 + +--- + + +## 特点 +- 丰富的事件系统 +- 命名不那么乱七八糟 +- 隐藏开发者不需要关注的细节 +- 根据支付宝、微信最新 API 开发而成 +- 高度抽象的类,免去各种拼json与xml的痛苦 +- 符合 PSR 标准,你可以各种方便的与你的框架集成 +- 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关 +- 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的 + + +## 支持的支付方法 +### 1、支付宝 +- 电脑支付 +- 手机网站支付 +- APP 支付 +- 刷卡支付 +- 扫码支付 +- 账户转账 +- 小程序支付 + +| method | 描述 | +| :-------: | :-------: | +| web | 电脑支付 | +| wap | 手机网站支付 | +| app | APP 支付 | +| pos | 刷卡支付 | +| scan | 扫码支付 | +| transfer | 帐户转账 | +| mini | 小程序支付 | + +### 2、微信 +- 公众号支付 +- 小程序支付 +- H5 支付 +- 扫码支付 +- 刷卡支付 +- APP 支付 +- 企业付款 +- 普通红包 +- 分裂红包 + +| method | 描述 | +| :-----: | :-------: | +| mp | 公众号支付 | +| miniapp | 小程序支付 | +| wap | H5 支付 | +| scan | 扫码支付 | +| pos | 刷卡支付 | +| app | APP 支付 | +| transfer | 企业付款 | +| redpack | 普通红包 | +| groupRedpack | 分裂红包 | + + +## 支持的方法 +所有网关均支持以下方法 + +- find(array/string $order) +说明:查找订单接口 +参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。 +返回:查询成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- refund(array $order) +说明:退款接口 +参数:`$order` 数组格式,退款参数。 +返回:退款成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- cancel(array/string $order) +说明:取消订单接口 +参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。 +返回:取消成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- close(array/string $order) +说明:关闭订单接口 +参数:`$order` 为 `string` 类型时,请传入系统订单号,对应支付宝或微信中的 `out_trade_no`; `array` 类型时,参数请参考支付宝或微信官方文档。 +返回:关闭成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- verify() +说明:验证服务器返回消息是否合法 +返回:验证成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据。 +异常:`GatewayException` 或 `InvalidSignException` + +- PAYMETHOD(array $order) +说明:进行支付;具体支付方法名称请参考「支持的支付方法」一栏 +返回:成功,返回 `Yansongda\Supports\Collection` 实例,可以通过 `$colletion->xxx` 或 `$collection['xxx']` 访问服务器返回的数据或 `Symfony\Component\HttpFoundation\Response` 实例,可通过 `return $response->send()`(laravel 框架中直接 `return $response`) 返回,具体请参考文档。 +异常:`GatewayException` 或 `InvalidSignException` + + +## 错误 + +如果在调用相关支付网关 API 时有错误产生,会抛出 `GatewayException`,`InvalidSignException` 错误,可以通过 `$e->getMessage()` 查看,同时,也可通过 `$e->raw` 查看调用 API 后返回的原始数据,该值为数组格式。 + + +## 所有异常 + +* Yansongda\Pay\Exceptions\InvalidGatewayException ,表示使用了除本 SDK 支持的支付网关。 +* Yansongda\Pay\Exceptions\InvalidSignException ,表示验签失败。 +* Yansongda\Pay\Exceptions\InvalidConfigException ,表示缺少配置参数,如,`ali_public_key`, `private_key` 等。 +* Yansongda\Pay\Exceptions\GatewayException ,表示支付宝/微信服务器返回的数据非正常结果,例如,参数错误,对账单不存在等。 diff --git a/web/docs/v2/installation.md b/web/docs/v2/installation.md new file mode 100644 index 0000000..726c51b --- /dev/null +++ b/web/docs/v2/installation.md @@ -0,0 +1,13 @@ +# 安装 + +## 运行环境 +- PHP 7.0+ (v2.8.0 开始 >= 7.1.3) +- composer + +> php5 请使用 v1.x 版本[https://github.com/yansongda/pay/tree/v1.x](https://github.com/yansongda/pay/tree/v1.x) + + +## 安装方式 +```shell +composer require yansongda/pay:^2.10 -vvv +``` diff --git a/web/docs/v2/logger/usage.md b/web/docs/v2/logger/usage.md new file mode 100644 index 0000000..1b79e05 --- /dev/null +++ b/web/docs/v2/logger/usage.md @@ -0,0 +1,26 @@ +# 日志系统 + +SDK 自带日志系统,如果需要指定日志文件或日志级别,请 config 中传入下列参数。如果不传入,默认为 `warning` 级别,日志路径在 `sys_get_temp_dir().'/logs/yansongda.pay.log' ` + +## 配置 + +```php +'log' => [ + 'file' => './logs/pay.log', // 请注意权限 + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily, daily 时将按时间自动划分文件. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 +], +``` + +## 使用 + +:::tip +使用日志功能前,请先确认已经使用过支付等功能进行了初始化! +::: + +```php +use Yansongda\Pay\Log; + +Log::debug('Paying...', $order->all()); +``` diff --git a/web/docs/v2/others/faq.md b/web/docs/v2/others/faq.md new file mode 100644 index 0000000..b025dbd --- /dev/null +++ b/web/docs/v2/others/faq.md @@ -0,0 +1,29 @@ +# FAQ + +1. 支付宝支付成功,但是对返回数据验签老是失败。 + + 绝大部分问题都是配置问题,首先请确认 **正确填写了支付宝公钥(注意不是应用公钥)**。 + + 如果还没解决,请参考 github 的 issue:[https://github.com/yansongda/pay/issues/73](https://github.com/yansongda/pay/issues/73) + + 如果仍然没解决,请检查您所使用的框架是否对 get/post 数据进行了增加,请自行处理好 Nginx/Apache 对框架的 URL 重写问题 + +2. 我是做 API 的,怎样可以不用 send 出 Response 中的内容? + + 返回的 Response 中,`$response->getContent()` 可获取内容,RedirectResponse 中,`$response->getTargetUrl()` 可获取跳转链接,JsonResponse 中,`$response->getContent()` 可获取Json。 + +3. cURL error 60: SSL certificate problem: unable to get local issuer certificate + + 服务器环境配置问题。 + + 请参考 [https://github.com/yansongda/pay/issues/16](https://github.com/yansongda/pay/issues/16) + +4. 是否支持其他支付平台?比如:银联、京东。 + + 由于使用限制,暂不支持。 + + **欢迎 PR!** + +5. 支付宝是否支持 AES 等加密方式? + + 因为支付宝推荐 RSA2 ,所以不推荐也不支持除 **RSA2** 以外的任何加密方式! diff --git a/web/docs/v2/quickUsage.md b/web/docs/v2/quickUsage.md new file mode 100644 index 0000000..3d7436b --- /dev/null +++ b/web/docs/v2/quickUsage.md @@ -0,0 +1,149 @@ +# 快速上手 + +## 支付宝 +```php + '2016082000295641', + 'notify_url' => 'http://yansongda.cn/notify.php', + 'return_url' => 'http://yansongda.cn/return.php', + 'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB', + // 加密方式: **RSA2** + 'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==', + 'log' => [ // optional + 'file' => './logs/alipay.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + 'mode' => 'dev', // optional,设置此参数,将进入沙箱模式 + ]; + + public function index() + { + $order = [ + 'out_trade_no' => time(), + 'total_amount' => '1', + 'subject' => 'test subject - 测试', + ]; + + $alipay = Pay::alipay($this->config)->web($order); + + return $alipay->send();// laravel 框架中请直接 `return $alipay` + } + + public function return() + { + $data = Pay::alipay($this->config)->verify(); // 是的,验签就这么简单! + + // 订单号:$data->out_trade_no + // 支付宝交易号:$data->trade_no + // 订单总金额:$data->total_amount + } + + public function notify() + { + $alipay = Pay::alipay($this->config); + + try{ + $data = $alipay->verify(); // 是的,验签就这么简单! + + // 请自行对 trade_status 进行判断及其它逻辑进行判断,在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。 + // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号; + // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额); + // 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email); + // 4、验证app_id是否为该商户本身。 + // 5、其它业务逻辑情况 + + Log::debug('Alipay notify', $data->all()); + } catch (\Exception $e) { + // $e->getMessage(); + } + + return $alipay->success()->send();// laravel 框架中请直接 `return $alipay->success()` + } +} +``` + + +## 微信 +```php + 'wxb3fxxxxxxxxxxx', // APP APPID + 'app_id' => 'wxb3fxxxxxxxxxxx', // 公众号 APPID + 'miniapp_id' => 'wxb3fxxxxxxxxxxx', // 小程序 APPID + 'mch_id' => '14577xxxx', + 'key' => 'mF2suE9sU6Mk1Cxxxxxxxxxxx', + 'notify_url' => 'http://yanda.net.cn/notify.php', + 'cert_client' => './cert/apiclient_cert.pem', // optional,退款等情况时用到 + 'cert_key' => './cert/apiclient_key.pem',// optional,退款等情况时用到 + 'log' => [ // optional + 'file' => './logs/wechat.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + 'mode' => 'dev', // optional, dev/hk;当为 `hk` 时,为香港 gateway。 + ]; + + public function index() + { + $order = [ + 'out_trade_no' => time(), + 'total_fee' => '1', // **单位:分** + 'body' => 'test body - 测试', + 'openid' => 'onkVf1FjWS5SBIixxxxxxx', + ]; + + $pay = Pay::wechat($this->config)->mp($order); + + // $pay->appId + // $pay->timeStamp + // $pay->nonceStr + // $pay->package + // $pay->signType + } + + public function notify() + { + $pay = Pay::wechat($this->config); + + try{ + $data = $pay->verify(); // 是的,验签就这么简单! + + Log::debug('Wechat notify', $data->all()); + } catch (\Exception $e) { + // $e->getMessage(); + } + + return $pay->success()->send();// laravel 框架中请直接 `return $pay->success()` + } +} +``` diff --git a/web/docs/v2/sidebar.js b/web/docs/v2/sidebar.js new file mode 100644 index 0000000..7cea38d --- /dev/null +++ b/web/docs/v2/sidebar.js @@ -0,0 +1,62 @@ +exports = module.exports = [ + { + text: '快速入门', + collapsible: true, + items: [ + { text: '安装', link: '/docs/v2/installation'}, + { text: '快速上手', link: '/docs/v2/quickUsage'}, + { text: '贡献', link: '/docs/v2/contribute'} + ] + }, + { + text: '支付 - 支付宝', + collapsible: true, + items: [ + { text: '概述', link: '/docs/v2/alipay/index'}, + { text: '支付', link: '/docs/v2/alipay/pay'}, + { text: '查询', link: '/docs/v2/alipay/find'}, + { text: '取消', link: '/docs/v2/alipay/cancel'}, + { text: '关闭', link: '/docs/v2/alipay/close'}, + { text: '退款', link: '/docs/v2/alipay/refund'}, + { text: '接收回调', link: '/docs/v2/alipay/callback'}, + { text: '确认回调', link: '/docs/v2/alipay/response'} + ] + }, + { + text: '支付 - 微信', + collapsible: true, + items: [ + { text: '概述', link: '/docs/v2/wechat/index'}, + { text: '支付', link: '/docs/v2/wechat/pay'}, + { text: '查询', link: '/docs/v2/wechat/find'}, + { text: '取消', link: '/docs/v2/wechat/cancel'}, + { text: '关闭', link: '/docs/v2/wechat/close'}, + { text: '退款', link: '/docs/v2/wechat/refund'}, + { text: '接收回调', link: '/docs/v2/wechat/callback'}, + { text: '确认回调', link: '/docs/v2/wechat/response'} + ] + }, + { + text: '事件系统', + collapsible: true, + items: [ + { text: '概述', link: '/docs/v2/events/index'}, + { text: '说明', link: '/docs/v2/events/class'}, + { text: '使用', link: '/docs/v2/events/usage'} + ] + }, + { + text: '日志系统', + collapsible: true, + items: [ + { text: '使用', link: '/docs/v2/logger/usage'} + ] + }, + { + text: '其它', + collapsible: true, + items: [ + { text: 'FAQ', link: '/docs/v2/others/faq'} + ] + } +]; diff --git a/web/docs/v2/wechat/callback.md b/web/docs/v2/wechat/callback.md new file mode 100644 index 0000000..e001db9 --- /dev/null +++ b/web/docs/v2/wechat/callback.md @@ -0,0 +1,38 @@ +# 接收回调 + +| 方法名 | 参数 | 返回值 | +|:------:|:---:|:----------:| +| verify | 无 | Collection | + +## 支付异步通知验证 + +```php +$result = $wechat->verify(); +// 是的,你没有看错,就是这么简单! + +// return $wechat->success()->send(); // laravel 框架直接 return $wechat->success(); +``` + + +## 退款异步通知验证 + +:::tip +v2.4.0 及以上可用 +::: + +```php +$result = $wechat->verify(null, true); +// 是的,你没有看错,就是这么简单! + +// return $wechat->success()->send(); // laravel 框架直接 return $wechat->success(); +``` + + +## 配置参数 + +无 + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/wechat/cancel.md b/web/docs/v2/wechat/cancel.md new file mode 100644 index 0000000..69a091a --- /dev/null +++ b/web/docs/v2/wechat/cancel.md @@ -0,0 +1,7 @@ +# 取消 + +微信官方无此 API,请调用 close 关闭订单。 + +## 异常 + +Yansongda\Pay\Exceptions\GatewayException :Wechat Do Not Have Cancel API! Plase use Close API! diff --git a/web/docs/v2/wechat/close.md b/web/docs/v2/wechat/close.md new file mode 100644 index 0000000..f68ce28 --- /dev/null +++ b/web/docs/v2/wechat/close.md @@ -0,0 +1,31 @@ +# 关闭 + +| 方法名 | 参数 | 返回值 | +|:-----:|:-------------------:|:----------:| +| close | string/array $order | Collection | + +## 例子 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; + +// $order = '1514027114'; + +$result = $wechat->close($order); +``` + + +## 订单配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3),查看「请求参数」一栏。 + +### APP/小程序订单关闭 + +如果您需要关闭 `APP/小程序` 的订单,请传入参数:`['type' => 'app']`/`['type' => 'miniapp']` + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/wechat/find.md b/web/docs/v2/wechat/find.md new file mode 100644 index 0000000..f9b815e --- /dev/null +++ b/web/docs/v2/wechat/find.md @@ -0,0 +1,68 @@ +# 查询 + +| 方法名 | 参数 | 返回值 | +|:----:|:--------------------------------------:|:----------:| +| find | string/array $order, string/null $type | Collection | + +## 查询普通支付订单 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = $wechat->find($order); +``` + + +## 查询退款订单 + +```php +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = $wechat->find($order, 'refund'); +``` + + +## 查询企业付款订单 + +:::tip +v2.7.8 及以上可用 +::: + +```php +$order = [ + 'partner_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = $wechat->find($order, 'transfer'); +``` + + +## 订单配置参数 + +### 查询订单 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2),查看「请求参数」一栏。 + +### 查询退款订单 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5),查看「请求参数」一栏。 + +### 查询企业付款订单 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3),查看「请求参数」一栏。 + +### APP/小程序查询 + +如果您需要查询 `APP/小程序` 的订单,请传入参数:`['type' => 'app']`/`['type' => 'miniapp']` + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/wechat/index.md b/web/docs/v2/wechat/index.md new file mode 100644 index 0000000..dc7ca3a --- /dev/null +++ b/web/docs/v2/wechat/index.md @@ -0,0 +1,109 @@ +# 概述 + +**请先熟悉 微信支付 开发文档!** + +## 快速上手 + +```php +use Yansongda\Pay\Pay; + +$config = [ + 'appid' => 'wxb3fxxxxxxxxxxx', // APP APPID + 'app_id' => 'wxb3fxxxxxxxxxxx', // 公众号 APPID + 'miniapp_id' => 'wxb3fxxxxxxxxxxx', // 小程序 APPID + 'mch_id' => '145776xxxx', + 'key' => 'mF2suE9sU6Mk1CxxxxIxxxxx', + 'notify_url' => 'http://yanda.net.cn', + 'cert_client' => './cert/apiclient_cert.pem', // optional, 退款,红包等情况时需要用到 + 'cert_key' => './cert/apiclient_key.pem',// optional, 退款,红包等情况时需要用到 + 'log' => [ // optional + 'file' => './logs/wechat.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], + // 'mode' => 'dev', +]; + +// 支付 +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', + 'openid' => 'onkVf1FjWS5SBxxxxxxxx', +]; + +$result = Pay::wechat($config)->mp($order); + +// 退款 +$order = [ + 'out_trade_no' => '1514192025', + 'out_refund_no' => time(), + 'total_fee' => '1', + 'refund_fee' => '1', + 'refund_desc' => '测试退款haha', +]; + +$result = Pay::wechat($config)->refund($order); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + +// 查询 +$result = Pay::wechat($config)->find('out_trade_no_123456'); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + +// 取消 +微信未提供取消订单接口,访问此接口将抛出 `GatewayException` 异常。 + +// 关闭 +$result = Pay::wechat($config)->close('out_trade_no_123456'); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$result->xxx` 访问服务器返回的数据。 + +// 验证服务器数据 +$wechat = Pay::wechat($config) + +// 是的,验签就这么简单! +$data = $wechat->verify(); // 返回 `Yansongda\Supports\Collection` 实例,可以通过 `$data->xxx` 访问服务器返回的数据。 + +$wechat->success()->send(); // 向微信服务器确认接收到的数据。laravel 框架中请直接 `return $wechat->success()` +``` + +## 服务商模式 + +> {info} 版本要求: version >= 2.1.0 + +config 配置参数如下。 + +```php +$config = [ + 'appid' => 'wxb3fxxxxxxxxxxx', // APP APPID + 'app_id' => 'wxb3fxxxxxxxxxxx', // 公众号 APPID + 'miniapp_id' => 'wxb3fxxxxxxxxxxx', // 小程序 APPID + 'sub_appid' => 'wxb3fxxxxxxxxxxx', // 子商户 APP APPID + 'sub_app_id' => 'wxb3fxxxxxxxxxxx', // 子商户 公众号 APPID + 'sub_miniapp_id' => 'wxb3fxxxxxxxxxxx', // 子商户 小程序 APPID + 'mch_id' => '146xxxxxx', // 商户号 + 'sub_mch_id' => '146xxxxxx', // 子商户商户号 + 'key' => '4e538260xxxxxxxxxxxxxxxxxxxxxx', // 主商户 key + 'notify_url' => 'http://yanda.net.cn/notify.php', + 'cert_client' => './cert/apiclient_cert.pem', // optional,退款等情况时用到 + 'cert_key' => './cert/apiclient_key.pem',// optional,退款等情况时用到 + 'log' => [ // optional + 'file' => './logs/wechat.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily, daily 时将按时间自动划分文件. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'mode' => 'service', +] +``` + +**说明:** 处于服务商模式下的时候,`appid`、`app_id`、`miniapp_id` 均为**主商户**的信息,`sub_` 开头的为**子服务商**的信息 + +详细请参考 [https://github.com/yansongda/pay/pull/82](https://github.com/yansongda/pay/pull/82) + + +## 注意 + +后续文档中,如果没有特别说明, `$wechat` 均代表`Pay::wechat($config)` diff --git a/web/docs/v2/wechat/pay.md b/web/docs/v2/wechat/pay.md new file mode 100644 index 0000000..c565b67 --- /dev/null +++ b/web/docs/v2/wechat/pay.md @@ -0,0 +1,249 @@ +# 支付 + +微信支付目前支持 9 种支付方法,对应的支付 method 如下: + +| method | 说明 | 参数 | 返回值 | +|:------------:|:------:|:------------:|:------------:| +| mp | 公众号支付 | array $order | Collection | +| wap | 手机网站支付 | array $order | Response | +| app | APP 支付 | array $order | JsonResponse | +| pos | 刷卡支付 | array $order | Collection | +| scan | 扫码支付 | array $order | Collection | +| transfer | 账户转账 | array $order | Collection | +| miniapp | 小程序支付 | array $order | Collection | +| redpack | 普通红包 | array $order | Collection | +| groupRedpack | 裂变红包 | array $order | Collection | + +## 公众号支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', + 'openid' => 'onkVf1FjWS5SBxxxxxxxx', +]; + +$result = Pay::wechat($config)->mp($order); +// 返回 Collection 实例。包含了调用 JSAPI 的所有参数,如appId,timeStamp,nonceStr,package,signType,paySign 等; +// 可直接通过 $result->appId, $result->timeStamp 获取相关值。 +// 后续调用不在本文档讨论范围内,请自行参考官方文档。 +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1),查看「请求参数」一栏。 + + +## 手机网站支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', +]; + +return $wechat->wap($order)->send(); // laravel 框架中请直接 return $wechat->wap($order) +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1),查看「请求参数」一栏。 + + +## APP 支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', +]; + +// 后续 APP 调用,调用方式不在本文档讨论范围内,请参考官方文档。 +return $wechat->app($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1),查看「请求参数」一栏。 + + +## 刷卡支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', + 'auth_code' => '1354804793001231564897', +]; + +$result = $wechat->pos($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1),查看「请求参数」一栏。 + + +## 扫码支付 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', +]; + +// 扫码支付使用 模式二 +$result = $wechat->scan($order); +// 二维码内容: $qr = $result->code_url; +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1),查看「请求参数」一栏。 + + +## 账户转账 + +### 例子 + +```php +$order = [ + 'partner_trade_no' => '', //商户订单号 + 'openid' => '', //收款人的openid + 'check_name' => 'NO_CHECK', //NO_CHECK:不校验真实姓名\FORCE_CHECK:强校验真实姓名 + // 're_user_name'=>'张三', //check_name为 FORCE_CHECK 校验实名的时候必须提交 + 'amount' => '1', //企业付款金额,单位为分 + 'desc' => '帐户提现', //付款说明 +]; + +$result = $wechat->transfer($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2),查看「请求参数」一栏。 + +### 使用 APP/小程序 账号转账 + +如果您需要通过 `APP/小程序` 的商户账号appid进行转账,请传入参数:`['type' => 'app']`/`['type' => 'miniapp']` + +### !注意! + +如果您在队列中使用,请自行传参 `spbill_create_ip`。 + + +## 小程序 + +### 例子 + +```php +$order = [ + 'out_trade_no' => time(), + 'body' => 'subject-测试', + 'total_fee' => '1', + 'openid' => 'onkVf1FjWS5SBxxxxxxxx', +]; + +$result = $wechat->miniapp($order); +// 返回 Collection 实例。包含了调用 JSAPI 的所有参数,如appId,timeStamp,nonceStr,package,signType,paySign 等; +// 可直接通过 $result->appId, $result->timeStamp 获取相关值。 +// 后续调用不在本文档讨论范围内,请自行参考官方文档。 +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1),查看「请求参数」一栏。 + + +## 普通红包 + +### 例子 + +```php +$order = [ + 'mch_billno' => '商户订单号', + 'send_name' => '商户名称', + 'total_amount' => '1', + 're_openid' => '用户openid', + 'total_num' => '1', + 'wishing' => '祝福语', + 'act_name' => '活动名称', + 'remark' => '备注', +]; + +$result = $wechat->redpack($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3),查看「请求参数」一栏。 + +### !注意! + +如果您在队列中使用,请自行传参 `client_ip`。 + + +## 裂变红包 + +### 例子 + +```php +$order = [ + 'mch_billno' => '商户订单号', + 'send_name' => '商户名称', + 'total_amount' => '1', + 're_openid' => '用户openid', + 'total_num' => '3', + 'wishing' => '祝福语', + 'act_name' => '活动名称', + 'remark' => '备注', +]; + +$result = $wechat->groupRedpack($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign`, `spbill_create_ip` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4),查看「请求参数」一栏。 + +## 返回值 + +**各支付方法返回值请参考顶部表格** + +返回只会返回两种类型 `Symfony\Component\HttpFoundation\Response` 或 `Yansongda\Supports\Collection` + +* 返回 Response 类型时,可以通过 `return $response->send()` 直接进行返回(laravel 框架中使用请直接`return $response` ) +* 返回 Collection 类型时,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/wechat/refund.md b/web/docs/v2/wechat/refund.md new file mode 100644 index 0000000..703e47d --- /dev/null +++ b/web/docs/v2/wechat/refund.md @@ -0,0 +1,34 @@ +# 退款 + +| 方法名 | 参数 | 返回值 | +|:------:|:------------:|:----------:| +| refund | array $order | Collection | + +## 退款操作 + +```php +$order = [ + 'out_trade_no' => '1514192025', + 'out_refund_no' => time(), + 'total_fee' => '1', + 'refund_fee' => '1', + 'refund_desc' => '测试退款haha', +]; + +$result = $wechat->refund($order); +``` + + +## 订单配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4),查看「请求参数」一栏。 + +### APP/小程序退款 + +如果您需要退 `APP/小程序` 的订单,请传入参数:`['type' => 'app']`/`['type' => 'miniapp']` + + + +## 返回值 + +返回 Collection 类型,可以通过 `$collection->xxx` 得到服务器返回的数据。 diff --git a/web/docs/v2/wechat/response.md b/web/docs/v2/wechat/response.md new file mode 100644 index 0000000..dd1b9d4 --- /dev/null +++ b/web/docs/v2/wechat/response.md @@ -0,0 +1,23 @@ +# 确认回调 + +| 方法名 | 参数 | 返回值 | +|:-------:|:---:|:--------:| +| success | 无 | Response | + +## 使用方法 + +```php +// $result = $wechat->verify(); + +return $wechat->success()->send(); // laravel 框架直接 return $wechat->success(); +``` + + +## 配置参数 + +无 + + +## 返回值 + +返回 Response 类型,可以通过`return $response->send();` 进行返回;如果在 laravel 框架中,可直接 `return $response;` diff --git a/web/docs/v3/alipay/callback.md b/web/docs/v3/alipay/callback.md new file mode 100644 index 0000000..03fe7ed --- /dev/null +++ b/web/docs/v3/alipay/callback.md @@ -0,0 +1,42 @@ +# 接收支付宝回调 + +| 方法名 | 参数 | 返回值 | +|:--------:|:------------------------------:|:----------:| +| callback | 无/array/ServerRequestInterface | Collection | + +使用的加密方式为支付宝官方推荐的 **RSA2**,目前只支持这一种加密方式,且没有支持其他加密方式的计划。 + +## 例子 + +```php +Pay::config($this->config); + +// 是的,你没有看错,就是这么简单! +$result = Pay::alipay()->callback(); +``` + +## 参数 + +### 第一个参数 + +#### `null` + +如果您没有传参,或传 `null` 则 `yansongda/pay` 会自动识别支付宝的回调请求并处理,通过 `Collection` 实例返回支付宝的处理参数 + +:::warning +建议仅在 php-fpm 下使用,swoole 方式请使用 `ServerRequestInterface` 参数传递方式 +::: + +#### `ServerRequestInterface` + +推荐在 swoole 环境下传递此参数,传递此参数后, yansongda/pay 会自动进行后续处理 + +#### `array` + +也可以自行解析请求参数,传递一个 array 会自动进行后续处理 + +### 第二个参数 + +第二个参数主要是传递相关自定义变量的,类似于 `web()` 中的 `_config` / `_method` 等参数。 + +例如,如果你想在回调的时候使用非默认配置,则可以 `Pay::alipay()->callback(null, ['_config' => 'yansongda'])` 切换为 `yansongda` 这个租户的配置信息。 diff --git a/web/docs/v3/alipay/cancel.md b/web/docs/v3/alipay/cancel.md new file mode 100644 index 0000000..84fe42c --- /dev/null +++ b/web/docs/v3/alipay/cancel.md @@ -0,0 +1,22 @@ +# 取消订单 + +| 方法名 | 参数 | 返回值 | +|:------:|:-------------------:|:----------:| +| cancel | string/array $order | Collection | + +## 例子 + +```php +Pay::config($this->config); + +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = Pay::alipay()->cancel($order); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.cancel),查看「请求参数」一栏。 diff --git a/web/docs/v3/alipay/close.md b/web/docs/v3/alipay/close.md new file mode 100644 index 0000000..6839674 --- /dev/null +++ b/web/docs/v3/alipay/close.md @@ -0,0 +1,19 @@ +# 关闭订单 + +| 方法名 | 参数 | 返回值 | +|:-----:|:-------------------:|:----------:| +| close | string/array $order | Collection | + +## 例子 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->close([ + 'out_trade_no' => '1623161325', +]); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.close),查看「请求参数」一栏。 diff --git a/web/docs/v3/alipay/download.md b/web/docs/v3/alipay/download.md new file mode 100644 index 0000000..da3b88d --- /dev/null +++ b/web/docs/v3/alipay/download.md @@ -0,0 +1,27 @@ +# 下载对账单 + +| 方法名 | 参数 | 返回值 | +|:--------:|:------------------:|:------:| +| download | string/array $bill | string | + +## 例子 + +```php +$bill = [ + 'bill_date' => '2016-04-05', // 2016-04 + 'bill_type' => 'trade' +]; + +// $bill = '2016-04-05'; + +$url = $alipay->download($bill); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://docs.open.alipay.com/api_15/alipay.data.dataservice.bill.downloadurl.query),查看「请求参数」一栏。 + + +## 返回值 + +返回 string 类型。直接返回账单下载链接。 diff --git a/web/docs/v3/alipay/find.md b/web/docs/v3/alipay/find.md new file mode 100644 index 0000000..77ad0ae --- /dev/null +++ b/web/docs/v3/alipay/find.md @@ -0,0 +1,54 @@ +# 查询订单 + +| 方法名 | 参数 | 返回值 | +|:----:|:-------------------:|:----------:| +| find | string/array $order | Collection | + +## 查询普通支付订单 + +```php +Pay::config($this->config); + +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = Pay::alipay()->find($order); +``` + +## 查询退款订单 + +```php +Pay::config($this->config); + +$order = [ + 'out_trade_no' => '1514027114', + 'out_request_no' => '1514027114', + '_type' => 'refund', +]; + +$result = Pay::alipay()->find($order); +``` + +## 查询转账订单 + +```php +Pay::config($this->config); + +$order = [ + 'out_trade_no' => '1514027114', + '_type' => 'transfer' +]; + +$result = Pay::alipay()->find($order); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考以下地址,查看「请求参数」一节。 + +- 支付订单:[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.query) +- 退款订单:[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query) +- 转账订单:[这里](https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.order.query) + diff --git a/web/docs/v3/alipay/more.md b/web/docs/v3/alipay/more.md new file mode 100644 index 0000000..a015117 --- /dev/null +++ b/web/docs/v3/alipay/more.md @@ -0,0 +1,137 @@ +# 更多方便的插件 + +得益于 yansongda/pay 的基础架构和良好的插件机制, +您可以自有的使用任何内置插件和自定义插件调用支付宝的任何 API。 + +诸如签名、API调用、解密、验签、解包等基础插件已经内置在 Pay 中, +您可以使用 `Pay::alipay()->mergeCommonPlugins(array $plugins)` 来获取调用 API 所必须的常用插件 + +首先,查找你想使用的插件,然后 + +```php +Pay::config($config); + +$params = [ + 'out_trade_no' => '1514027114', +]; + +$allPlugins = Pay::alipay()->mergeCommonPlugins([QueryPlugin::class]); + +$result = Pay::alipay()->pay($allPlugins, $params); +``` + +关于插件的详细介绍,如果您感兴趣,可以参考 [这篇说明文档](/docs/v3/kernel/plugin.md) + +## 账务API插件 + +### 下载对账单 + +- `Yansongda\Pay\Plugin\Alipay\Data\BillDownloadUrlQueryPlugin` + +### 申请电子回单 + +- `Yansongda\Pay\Plugin\Alipay\Data\BillEreceiptApplyPlugin` + +### 查询电子回单状态 + +- `Yansongda\Pay\Plugin\Alipay\Data\BillEreceiptQueryPlugin` + +## 生活缴费API插件 + +### 缴费直连代扣订单支付状态查询 + +- `Yansongda\Pay\Plugin\Alipay\Ebpp\PdeductBillStatusPlugin` + +### 公共事业缴费直连代扣扣款支付接口 + +- `Yansongda\Pay\Plugin\Alipay\Ebpp\PdeductPayPlugin` + +### 缴费直连代扣签约 + +- `Yansongda\Pay\Plugin\Alipay\Ebpp\PdeductSignAddPlugin` + +### 缴费直连代扣取消签约 + +- `Yansongda\Pay\Plugin\Alipay\Ebpp\PdeductSignCancelPlugin` + +## 资金API插件 + +### 支付宝资金账户资产查询接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\AccountQueryPlugin` + +### 资金授权冻结接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\AuthOrderFreezePlugin` + +### 资金授权解冻接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\AuthOrderUnfreezePlugin` + +### 查询转账订单接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\TransOrderQueryPlugin` + +### 转账业务单据查询接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\TransCommonQueryPlugin` + +### 资金转账页面支付接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\TransPagePayPlugin` + +:::warning +该插件需配合 `HtmlResponsePlugin` 插件一起使用 +::: + +### 单笔转账接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\TransUniTransferPlugin` + +### 单笔转账到银行账户接口 + +- `Yansongda\Pay\Plugin\Alipay\Fund\TransTobankTransferPlugin` + +## 工具类API + +### 换取授权访问令牌 + +- `Yansongda\Pay\Plugin\Alipay\Tools\SystemOauthTokenPlugin` + +### 换取应用授权令牌 + +- `Yansongda\Pay\Plugin\Alipay\Tools\OpenAuthTokenAppPlugin` + +### 查询某个应用授权AppAuthToken的授权信息 + +- `Yansongda\Pay\Plugin\Alipay\Tools\OpenAuthTokenAppQueryPlugin` + +## 会员API + +### 支付宝会员授权信息查询接口 + +- `Yansongda\Pay\Plugin\Alipay\User\InfoSharePlugin` + +### 支付宝个人协议页面签约接口 + +- `Yansongda\Pay\Plugin\Alipay\User\AgreementPageSignPlugin` + +:::warning +该插件需配合 `HtmlResponsePlugin` 插件一起使用 +::: + +### 支付宝个人代扣协议查询接口 + +- `Yansongda\Pay\Plugin\Alipay\User\AgreementQueryPlugin` + +### 支付宝个人代扣协议解约接口 + +- `Yansongda\Pay\Plugin\Alipay\User\AgreementUnsignPlugin` + +### 周期性扣款协议执行计划修改接口 + +- `Yansongda\Pay\Plugin\Alipay\User\AgreementExecutionPlanModifyPlugin` + +### 协议由普通通用代扣协议产品转移到周期扣协议产品 + +- `Yansongda\Pay\Plugin\Alipay\User\AgreementTransferPlugin` diff --git a/web/docs/v3/alipay/pay.md b/web/docs/v3/alipay/pay.md new file mode 100644 index 0000000..12df0ed --- /dev/null +++ b/web/docs/v3/alipay/pay.md @@ -0,0 +1,208 @@ +# 支付 + +支付宝支付目前直接内置支持 7 种快捷方式支付方法,对应的支付 method 如下: + +| method | 说明 | 参数 | 返回值 | +|:--------:|:------:|:------------:|:----------:| +| web | 电脑支付 | array $order | Response | +| wap | 手机网站支付 | array $order | Response | +| app | APP 支付 | array $order | Response | +| pos | 刷卡支付 | array $order | Collection | +| scan | 扫码支付 | array $order | Collection | +| transfer | 账户转账 | array $order | Collection | +| mini | 小程序支付 | array $order | Collection | + +更多接口调用请参考后续文档 + +## 电脑支付 + +### 例子 + +```php +Pay::config($this->config); + +return Pay::alipay()->web([ + 'out_trade_no' => ''.time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 1', +]); +``` + +#### GET 方式提交 + +为您考虑到了这一点,如果您想使用 GET 方式提交请求,可以在参数中增加 `['_method' => 'get']` 即可,例如 + +```php +Pay::config($this->config); + +return Pay::alipay()->web([ + 'out_trade_no' => ''.time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 1', + '_method' => 'get', +]); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay),查看「请求参数」一栏。 + +## 手机网站支付 + +### 例子 + +```php +Pay::config($this->config); + +return Pay::alipay()->wap([ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', + 'quit_url' => 'https://yansongda.cn', + ]); +``` + +#### GET 方式提交 + +为您考虑到了这一点,如果您想使用 GET 方式提交请求,可以在参数中增加 `['_method' => 'get']` 即可,例如 + +```php +Pay::config($this->config); + +return Pay::alipay()->wap([ + 'out_trade_no' => ''.time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 1', + '_method' => 'get', +]); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay),查看「请求参数」一栏。 + +## APP 支付 + +### 例子 + +```php +Pay::config($this->config); + +// 后续 APP 调用方式不在本文档讨论范围内,请参考官方文档。 +return Pay::alipay()->app([ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', +]); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay),查看「请求参数」一栏。 + +## 小程序支付 + +### 例子 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->mini([ + 'out_trade_no' => time().'', + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', + 'buyer_id' => '2088622190161234', +]); + +return $result->get('trade_no'); // 支付宝交易号 +// return $result->trade_no; +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.create/),查看「请求参数」一栏。 + +小程序支付接入文档:[https://docs.alipay.com/mini/introduce/pay](https://opendocs.alipay.com/mini/introduce/pay)。 + + +## 刷卡支付 + +### 例子 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->pos([ + 'out_trade_no' => time(), + 'auth_code' => '284776044441477959', + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', +]); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.pay),查看「请求参数」一栏。 + +## 扫码支付 + +### 例子 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->scan([ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', +]); + +return $result->qr_code; // 二维码 url +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`product_code` 等参数。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate),查看「请求参数」一栏。 + +## 账户转账 + +### 例子 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->transfer([ + 'out_biz_no' => '202106051432', + 'trans_amount' => '0.01', + 'product_code' => 'TRANS_ACCOUNT_NO_PWD', + 'biz_scene' => 'DIRECT_TRANSFER', + 'payee_info' => [ + 'identity' => 'ghdhjw7124@sandbox.com', + 'identity_type' => 'ALIPAY_LOGON_ID', + 'name' => '沙箱环境' + ], +]); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了** + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer),查看「请求参数」一栏。 + + +:::tip +转账查询等,请参考 [查询](/docs/v3/alipay/find.md) +::: diff --git a/web/docs/v3/alipay/refund.md b/web/docs/v3/alipay/refund.md new file mode 100644 index 0000000..d13b77b --- /dev/null +++ b/web/docs/v3/alipay/refund.md @@ -0,0 +1,20 @@ +# 退款 + +| 方法名 | 参数 | 返回值 | +|:------:|:------------:|:----------:| +| refund | array $order | Collection | + +## 退款操作 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->refund([ + 'out_trade_no' => '1623160012', + 'refund_amount' => '0.01', +]); +``` + +## 配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://opendocs.alipay.com/apis/api_1/alipay.trade.refund),查看「请求参数」一栏。 diff --git a/web/docs/v3/alipay/response.md b/web/docs/v3/alipay/response.md new file mode 100644 index 0000000..87fc698 --- /dev/null +++ b/web/docs/v3/alipay/response.md @@ -0,0 +1,19 @@ +# 确认回调 + +| 方法名 | 参数 | 返回值 | +|:-------:|:---:|:--------:| +| success | 无 | Response | + +## 例子 + +```php +Pay::config($config); + +// $result = Pay::alipay()->callback(); + +return Pay::alipay()->success(); +``` + +## 订单配置参数 + +无 diff --git a/web/docs/v3/index.md b/web/docs/v3/index.md new file mode 100644 index 0000000..3ea35ba --- /dev/null +++ b/web/docs/v3/index.md @@ -0,0 +1,45 @@ +# 介绍 + +

+Logo +

+ +该文档为 v3 版本的文档,如果您正在使用 v2.x 版本的 SDK,请您传送至 [这里](/docs/v2/)。 + +v3 版与 v2 版在底层有很大的不同,基础架构做了重新的设计,更易扩展,使用起来更方便。 + +## 前言 + +开发了多次支付宝与微信支付后,很自然产生一种反感,惰性又来了,想在网上找相关的轮子,可是一直没有找到一款自己觉得逞心如意的,要么使用起来太难理解,要么文件结构太杂乱,只有自己撸起袖子干了。 + +欢迎 Star,欢迎 PR! + +hyperf 扩展包请 [传送至这里](https://github.com/yansongda/hyperf-pay) + +laravel 扩展包请 [传送至这里](https://github.com/yansongda/laravel-pay) + +yii 扩展包请 [传送至这里](https://github.com/guanguans/yii-pay) + +## 特点 + +- 多租户支持 +- Swoole 支持 +- 灵活的插件机制 +- 丰富的事件系统 +- 命名不那么乱七八糟 +- 隐藏开发者不需要关注的细节 +- 根据支付宝、微信最新 API 开发而成 +- 高度抽象的类,免去各种拼json与xml的痛苦 +- 文件结构清晰易理解,可以随心所欲添加本项目中没有的支付网关 +- 方法使用更优雅,不必再去研究那些奇怪的的方法名或者类名是做啥用的 +- 内置自动获取微信公共证书方法,再也不用再费劲去考虑第一次获取证书的的问题了 +- 符合 PSR2、PSR3、PSR4、PSR7、PSR11、PSR14、PSR18 等各项标准,你可以各种方便的与你的框架集成 + +## 运行环境 + +- PHP 7.3+ (v3.1.0 开始需 7.4+) +- composer + +## LICENSE + +MIT diff --git a/web/docs/v3/kernel/pipeline.md b/web/docs/v3/kernel/pipeline.md new file mode 100644 index 0000000..c6ea9f2 --- /dev/null +++ b/web/docs/v3/kernel/pipeline.md @@ -0,0 +1,4 @@ +# 🧪Pipeline + +yansongda/pay 最主要的 plugin 机制,其实就是借助于 `pipeline` 来实现的, +网络上有很多关于 pipeline 的说明,在此不再赘述,感兴趣的同学可以查阅相关资料。 diff --git a/web/docs/v3/kernel/plugin.md b/web/docs/v3/kernel/plugin.md new file mode 100644 index 0000000..f50959a --- /dev/null +++ b/web/docs/v3/kernel/plugin.md @@ -0,0 +1,364 @@ +# 🔌Plugin + +得益于 pipeline,Pay 中的所有数据变换都通过 plugin 来实现, +同时 Pay 中也内置了很多常用的 Plugin,因此使用方式非常灵活简单。 + +其实大家经常使用的 「网站支付」「小程序支付」「查询订单」 等均属于自定义插件,只不过这类插件已经内置在 yansongda/pay 中了,不需要您额外开发即可使用。 + +## 定义 + +```php + $rocket]); + + $rocket->setDirection(ResponseParser::class) + ->mergePayload([ + 'method' => 'alipay.trade.page.pay', + 'biz_content' => array_merge( + ['product_code' => 'FAST_INSTANT_TRADE_PAY'], + $rocket->getParams() + ), + ]); + + Logger::info('[alipay][PagePayPlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } +} +``` + +这个 Plugin 的目的就是为了组装一系列支付宝所需要的参数,同时,由于电脑支付是不需要后端 http 调用支付宝接口的, +只需要一个浏览器的响应,所以,我们把 🚀 的 `Direction` 设置成了 `ResponseParser::class`。 + +- 跳转响应 Plugin + +```php + $rocket]); + + /* @var Rocket $rocket */ + $rocket = $next($rocket); + + $radar = $rocket->getRadar(); + + $response = 'GET' === $radar->getMethod() ? + $this->buildRedirect($radar->getUri()->__toString(), $rocket->getPayload()) : + $this->buildHtml($radar->getUri()->__toString(), $rocket->getPayload()); + + $rocket->setDestination($response); + + Logger::info('[alipay][HtmlResponsePlugin] 插件装载完毕', ['rocket' => $rocket]); + + return $rocket; + } + + protected function buildRedirect(string $endpoint, Collection $payload): Response + { + $url = $endpoint.'?'.Arr::query($payload->all()); + + $content = sprintf(' + + + + + + Redirecting to %1$s + + + Redirecting to %1$s. + + ', htmlspecialchars($url, ENT_QUOTES) + ); + + return new Response(302, ['Location' => $url], $content); + } + + protected function buildHtml(string $endpoint, Collection $payload): Response + { + $sHtml = "
"; + foreach ($payload->all() as $key => $val) { + $val = str_replace("'", ''', $val); + $sHtml .= ""; + } + $sHtml .= "
"; + $sHtml .= ""; + + return new Response(200, [], $sHtml); + } +} +``` + +在处理好支付宝所需要的参数之后,按照其它正常逻辑,应该调用支付宝API获取数据了, +但是由于 电脑支付 不是直接调用支付宝API的, +所以,这里使用了 `后置 plugin` 处理组装相关 html 代码进行 post 或者 GET 请求访问支付宝电脑支付页面。 + +最后,得益于 🚀 的 `Direction` 机制,最终返回给你的就是一个符合 PSR7 规范的 `Response` 对象了, +您可以集成到任何符合相关规范的框架中。 + +### 支付宝查询订单 + +```php + $rocket]); + + $rocket->mergePayload([ + 'method' => $this->getMethod(), + 'biz_content' => $rocket->getParams(), + ]); + + Logger::info('[alipay][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + abstract protected function getMethod(): string; +} +``` + +通过以上代码,我们大概能明白,查询订单的 `QueryPlugin` 插件,继承了 `GeneralPlugin` 这个常用插件, +通过支付宝官方文档,我们知道,查询订单的 API 将传参中的 method 改为了 `alipay.trade.query`,其它参数均是个性化参数,和入参有关, +因此,我们在做查询订单时,是需要简单的把 method 按要求更改即可,是不是很简单? + +### 微信查询订单 + +```php +getParams()); + $payload = $rocket->getPayload(); + + if (is_null($payload->get('transaction_id'))) { + throw new InvalidParamsException(InvalidParamsException::MISSING_NECESSARY_PARAMS); + } + + return 'v3/pay/transactions/id/'. + $payload->get('transaction_id'). + '?mchid='.$config->get('mch_id', ''); + } + + protected function getMethod(): string + { + return 'GET'; + } + + protected function doSomething(Rocket $rocket): void + { + $rocket->setPayload(null); + } +} + +``` + +```php + $rocket]); + + $rocket->setRadar($this->getRequest($rocket)); + $this->doSomething($rocket); + + Logger::info('[wechat][GeneralPlugin] 通用插件装载完毕', ['rocket' => $rocket]); + + return $next($rocket); + } + + /** + * @throws \Yansongda\Pay\Exception\ContainerDependencyException + * @throws \Yansongda\Pay\Exception\ContainerException + * @throws \Yansongda\Pay\Exception\ServiceNotFoundException + */ + protected function getRequest(Rocket $rocket): RequestInterface + { + return new Request( + $this->getMethod(), + $this->getUrl($rocket), + $this->getHeaders(), + ); + } + + protected function getMethod(): string + { + return 'POST'; + } + + /** + * @throws \Yansongda\Pay\Exception\ContainerDependencyException + * @throws \Yansongda\Pay\Exception\ContainerException + * @throws \Yansongda\Pay\Exception\ServiceNotFoundException + */ + protected function getUrl(Rocket $rocket): string + { + $config = get_wechat_config($rocket->getParams()); + + return Wechat::URL[$config->get('mode', Pay::MODE_NORMAL)]. + $this->getUri($rocket); + } + + protected function getHeaders(): array + { + return [ + 'Content-Type' => 'application/json', + ]; + } + + abstract protected function doSomething(Rocket $rocket): void; + + abstract protected function getUri(Rocket $rocket): string; +} +``` + +支付宝和微信的 `QueryPlugin` 和 `GeneralPlugin` 有些许不一样,不过都是为了高度抽象出支付运营商的API。 + +通过微信官方文档,我们知道,查询订单的 API 将传参中的 url 是随参数变化而变化的,因此我们抽象出了 `getUri` 等方法,方便做各种请求上的调整。 + + +## 通用插件 + +Pay 内部已经集成了很多通用插件,如 加密,签名,调用支付宝/微信接口等。 + +只需要简单的使用以下代码即可获取通用插件 + +```php +$allPlugins = Pay::alipay()->mergeCommonPlugins([QueryPlugin::class]); +``` + +## 最终调用 + +在拿到所有的插件之后,就可以愉快的进行调用获取最后的数据了。 + +```php +$result = Pay::alipay()->pay($allPlugins, $params); +``` + +代码中的 `$params` 为调用 API 所需要的其它参数。 diff --git a/web/docs/v3/kernel/rocket.md b/web/docs/v3/kernel/rocket.md new file mode 100644 index 0000000..af47270 --- /dev/null +++ b/web/docs/v3/kernel/rocket.md @@ -0,0 +1,90 @@ +# 🚀Rocket + +yansongda/pay 将 `输入参数`、`请求`、`原始响应`、`最终响应` 抽象成了一种类型 `Rocket`, +在 Pay 项目中,所有的数据扭转都是通过 Rocket 来实现的。 + +`Rocket` 也是由不同的属性组成的,现一一道来 + +## 📡 Radar + +可以理解为是 🚀 的 `雷达`,负责导航使用。 + +该属性,实际上最终为一个 `\Psr\Http\Message\RequestInterface` 对象, +具体到项目,默认情况下,就是 `\Yansongda\Pay\Request`。 + +所有需要请求支付供应商 API 的方法,最终都会使用这个 📡 ,去调用 http 接口请求支付供应商的 API。 + +## 🛠 Params + +该属性为 `array`,实际存储的就是输入的所有参数。 + +例如 查询退款 中的 + +```php +[ + 'out_trade_no' => '1514027114', + 'out_request_no' => '1514027114', + '_type' => 'refund', +]; +``` + +Pay 项目中,将所有以 _下划线_ 开始的参数都定义为 `特殊参数`,此类参数 **一定不会在 payload 中出现**, +仅会用于特殊参数判断。 + +## ⚙️ Payload + +该属性类型为 `\Yansongda\Supports\Collection`,是 🚀 的 `有效载荷`, +实际存储的是所有需要请求给支付供应商的所有参数。 + +:::warning +注意,payload 和 params 是不一样的,params 存储的是原封不动的输入参数,payload 是经过一系列插件 "过滤" 后的 有效载荷。 +::: + +## 🗝 Direction + +🚀 的方向。 + +实际的作用为:把控最终请求需要解包的类型 + +例如,支付宝电脑支付中,其最终返回的是一个 `Response` 对象,不需要直接后端 http 请求支付宝接口的, +所以当使用支付宝电脑支付时,其 Direction 为 `Yansongda\Pay\Parser\ResponseParser::class`。 + +绝大多数情况下,均默认为:`Yansongda\Pay\Parser\CollectionParser::class` + +## 🌟 ️Destination + +🚀 的目的地。 + +实际作用为:最终返回的类 + +`Destination` 和 `Direction` 密切相关。 `Direction` 直接决定这 `Destination` 的值和类型。 + +- 当 Direction 为 CollectionParser 时 + + Destination 最终返回的是 Collection 对象 + +- 当 Direction 为 ResponseParser 时 + + Destination 最终返回的是 Response 对象 + +- 当 Direction 为 ArrayParser 时 + + Destination 最终返回的是 array 数组 + +- 当 Direction 为 JsonParser 时 + + Destination 最终返回的是 string + +- 当 Direction 为 NoHttpRequestParser 时 + + Destination 最终返回的是 原样的 Radar + +- 当 Direction 为 OriginResponseParser 时 + + Destination 最终返回的是 Rocket 中的 DestinationOrigin + +## ✨ DestinationOrigin + +🚀 的原始目的地。 + +实际作用为:当请求支付供应商的 http 接口后的原始 Response diff --git a/web/docs/v3/kernel/shortcut.md b/web/docs/v3/kernel/shortcut.md new file mode 100644 index 0000000..6c9de78 --- /dev/null +++ b/web/docs/v3/kernel/shortcut.md @@ -0,0 +1,53 @@ +# 💤快捷方式 + +Shortcut 即快捷方式,是一系列 Plugin 的组合,方便我们使用 Pay。 + +## 定义 + +```php + + * + * @return \Yansongda\Pay\Contract\PluginInterface[]|string[] + */ + public function getPlugins(array $params): array; +} +``` + +## 详细说明 + +以我们刚刚在 [插件Plugin](/docs/v3/kernel/plugin.md) 中的例子来说明, +支付宝电脑支付,其实也是一种 快捷方式 + +```php +params + + // coding to send email... + } +} + +// 2. 添加监听器 +Event::addListener(PayStarted::class, [new PayStartedListener(), 'sendEmail']); + +// 3. 喝杯咖啡 +``` + +## 事件 + +### 支付开始 + +- 事件类:Yansongda\Pay\Event\PayStarted +- 说明:此事件将在支付进入核心流程时进行抛出。此时 SDK 只进行了相关初始化操作,其它所有操作均未开始。 +- 额外数据: + - $rocket (相关参数) + - $plugins (所有使用的插件) + - $params (传递的原始参数) + +### 支付完毕 + +- 事件类:Yansongda\Pay\Event\PayFinish +- 说明:此事件将在所有参数处理完毕时抛出。 +- 额外数据: + - $rocket (相关参数) + +### 开始调用API + +- 事件类:Yansongda\Pay\Event\ApiRequesting +- 说明:此事件将在请求支付方的 API 前抛出。 +- 额外数据: + - $rocket (相关参数) + +### 调用API结束 + +- 事件类:Yansongda\Pay\Event\ApiRequested +- 说明:此事件将在请求支付方的 API 完成之后抛出。 +- 额外数据: + - $rocket (相关参数) + +### 收到通知 + +- 事件类:Yansongda\Pay\Event\CallbackReceived +- 说明:此事件将在收到支付方的请求(通常在异步通知或同步通知)时抛出 +- 额外数据: + - $provider (支付机构) + - $contents (收到的数据) + - $params (自定义数据) + +### 调用其它方法 + +- 事件类:Yansongda\Pay\Event\MethodCalled +- 说明:此事件将在调用除 PAYMETHOD 方法(例如,查询订单,退款,取消订单)时抛出 +- 额外数据: + - $provider (支付机构) + - $name (调用方法) + - $params (参数) diff --git a/web/docs/v3/others/faq.md b/web/docs/v3/others/faq.md new file mode 100644 index 0000000..15de7f6 --- /dev/null +++ b/web/docs/v3/others/faq.md @@ -0,0 +1,37 @@ +# FAQ + +1. 支付宝支付成功,但是对返回数据验签老是失败。 + + 绝大部分问题都是配置问题,首先请确认 **正确填写了支付宝公钥(注意不是应用公钥)**。 + + 如果还没解决,请参考 github 的 issue:[https://github.com/yansongda/pay/issues/73](https://github.com/yansongda/pay/issues/73) + + 如果仍然没解决,请检查您所使用的框架是否对 get/post 数据进行了增加,请自行处理好 Nginx/Apache 对框架的 URL 重写问题 + +2. 我是做 API 的,怎样可以获取 Response 中的内容? + + 返回的 Response 中,`(string) $response->getBody()` 可获取内容。详细请了解 PSR 规范。 + +3. cURL error 60: SSL certificate problem: unable to get local issuer certificate + + 服务器环境配置问题。 + + 请参考 [https://github.com/yansongda/pay/issues/16](https://github.com/yansongda/pay/issues/16) + +4. 是否支持其他支付平台?比如:银联、京东。 + + 由于使用限制,暂不支持。 + + **欢迎 PR!** + +5. 支付宝是否支持 AES 等加密方式? + + 因为支付宝推荐 RSA2 ,所以不推荐也不支持除 **RSA2** 以外的任何加密方式! + +6. 微信支付报错 Prepay Response Error: Missing PrepayId + + 预下单 失败了,一般是缺少参数或参数类型错误。可以自己捕获异常,看微信提示的详细信息,对照微信官方文档修改相关参数。 + +7. Get Wechat Public Cert Error + + 检查微信商户密钥(mch_secret_key),非 v2 秘钥 diff --git a/web/docs/v3/others/logger.md b/web/docs/v3/others/logger.md new file mode 100644 index 0000000..423818f --- /dev/null +++ b/web/docs/v3/others/logger.md @@ -0,0 +1,46 @@ +# 日志系统 + +## 配置 + +### 使用内置日志系统 + +:::tip +使用前,请确保已经安装了 `monolog/monolog`: `composer require monolog/monolog` +::: + +SDK 自带日志系统,如果需要指定日志文件或日志级别,请 config 中传入下列参数。 +如果不传入,则日志系统默认不启用。 + +```php +'logger' => [ + 'enable' => true, + 'file' => './logs/pay.log', // 请注意权限 + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily, daily 时将按时间自动划分文件. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 +], +``` + +### 使用外部日志系统 + +yansongda/pay 支持使用外部日志系统,只需要日志规范支持 PSR3 规范,实现了 `\Psr\Log\LoggerInterface` 即可。 + +```php +use Yansongda\Pay\Contract\LoggerInterface; + +// $logger = 你的日志系统 + +Pay::set(LoggerInterface::class, $logger); +``` + +## 使用 + +:::tip +使用日志功能前,请先确认已经使用过支付等功能进行了初始化! +::: + +```php +use Yansongda\Pay\Logger; + +Logger::debug('Paying...', $order->all()); +``` diff --git a/web/docs/v3/overview/business.md b/web/docs/v3/overview/business.md new file mode 100644 index 0000000..71fc617 --- /dev/null +++ b/web/docs/v3/overview/business.md @@ -0,0 +1,7 @@ +# 商业与企业服务 + +如果您有商业或企业技术支持服务的需求,以便您更轻松的使用 Pay,您可以将您的需求信息整理好后发邮件给我,如果合适,我会及时联系您。 + +:::tip +我的联系方式: me@yansongda.cn +::: diff --git a/web/docs/v3/overview/communication.md b/web/docs/v3/overview/communication.md new file mode 100644 index 0000000..556e695 --- /dev/null +++ b/web/docs/v3/overview/communication.md @@ -0,0 +1,23 @@ +# 线上交流 + +:::danger 注意 +进群前请仔细看文档,文档相对来说已经很详细了。 + +没有人义务回答你的问题,没有人欠你的,所以线上交流请文明用语,首先学会智慧提问、友好交流。 +::: + +## Issue + +[https://github.com/yansongda/pay/issues](https://github.com/yansongda/pay/issues) + +## Discuss + +[https://github.com/yansongda/pay/discussions](https://github.com/yansongda/pay/discussions) + +## QQ 群 + +:::warning +资金原因,仅提供 500 人群,请仔细阅读文档后,按需加入 +::: + +群号:690027516 diff --git a/web/docs/v3/overview/contribute.md b/web/docs/v3/overview/contribute.md new file mode 100644 index 0000000..df538a5 --- /dev/null +++ b/web/docs/v3/overview/contribute.md @@ -0,0 +1,7 @@ +# 参与开发 + +由于测试及使用环境的限制,本项目中只开发了「支付宝」和「微信支付」的相关支付网关。 + +如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,**_欢迎 Fork 并提交 PR!_** + +参与开发前,请先仔细阅读 [核心架构](/docs/v3/kernel/rocket.md) 以便了解相关核心思想。 diff --git a/web/docs/v3/overview/donate.md b/web/docs/v3/overview/donate.md new file mode 100644 index 0000000..2fa9e25 --- /dev/null +++ b/web/docs/v3/overview/donate.md @@ -0,0 +1,43 @@ +# 捐赠 + +Pay 项目采用 MIT 协议开源发布,任何人均可在遵循 MIT 协议的情况下随意使用。 + +但维护一个开源项目对于我来说并不容易,需要耗费大量时间、精力、财力。 +如果这个项目给您带来了方便,希望您赞助开发与可持续性发展。 +如果您愿意,请联系我,您的公司/个人赞助将展现在本页面上。 + +:::tip +我的联系方式: me@yansongda.cn +::: + +## 赏一杯瑞幸吧 + +![pay](/images/pay.jpg) + + +## 不完全名单 + +### 企业 + +[![jetbrains](/images/jetbrains.png)](https://www.jetbrains.com/) + +### 个人 + +排名按时间顺序 + +| 昵称 | 姓名 | 金额/用途 | +| ---- | ---- | --------- | +| 醒醒吧 | **平 | ¥10.00 | +| Akun | - | 提供微信支付测试资源帮助测试 | +| - | *坤 | ¥10.00 | +| - | *爆 | ¥66.00 | +| - | *洲 | ¥10.00 | +| - | *友 | ¥300.00 | +| 一杯小蓝 | *彦 | ¥16.80 | +| u*e | - | ¥11.00 | +| N*s | - | ¥10.00 | +| *虫 | - | ¥100.00 | +| - | **波 | ¥10.00 | +| *※ | - | ¥8.88 | +| 往** | - | ¥19.80 | +| 建*u | - | ¥17.60 | diff --git a/web/docs/v3/overview/versions.md b/web/docs/v3/overview/versions.md new file mode 100644 index 0000000..3ec890b --- /dev/null +++ b/web/docs/v3/overview/versions.md @@ -0,0 +1,3 @@ +# 更新记录 + +see: [CHANGELOG.md](https://github.com/yansongda/pay/blob/master/CHANGELOG.md) 或 [Release](https://github.com/yansongda/pay/releases) diff --git a/web/docs/v3/quick-start/alipay.md b/web/docs/v3/quick-start/alipay.md new file mode 100644 index 0000000..46dad56 --- /dev/null +++ b/web/docs/v3/quick-start/alipay.md @@ -0,0 +1,141 @@ +# 支付宝快速入门 + +在初始化完毕后,就可以直接方便的享受 `yansongda/pay` 带来的便利了。 + +## 网页支付 + +```php +Pay::config($this->config); + +return Pay::alipay()->web([ + 'out_trade_no' => ''.time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 1', +]); +``` + +## H5支付 + +```php +Pay::config($this->config); + +return Pay::alipay()->wap([ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', + 'quit_url' => 'https://yansongda.cn', + ]); +``` + +## APP 支付 + +```php +Pay::config($this->config); + +return Pay::alipay()->app([ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', +]); +``` + +## 小程序支付 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->mini([ + 'out_trade_no' => time().'', + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', + 'buyer_id' => '2088622190161234', +]); + +return $result->get('trade_no'); // 支付宝交易号 +// return $result->trade_no; +``` + +## 刷卡支付 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->pos([ + 'out_trade_no' => time(), + 'auth_code' => '284776044441477959', + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', +]); +``` + +## 扫码支付 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->scan([ + 'out_trade_no' => time(), + 'total_amount' => '0.01', + 'subject' => 'yansongda 测试 - 01', +]); + +return $result->qr_code; // 二维码 url +``` + +## 转账 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->transfer([ + 'out_biz_no' => '202106051432', + 'trans_amount' => '0.01', + 'product_code' => 'TRANS_ACCOUNT_NO_PWD', + 'biz_scene' => 'DIRECT_TRANSFER', + 'payee_info' => [ + 'identity' => 'ghdhjw7124@sandbox.com', + 'identity_type' => 'ALIPAY_LOGON_ID', + 'name' => '沙箱环境' + ], +]); +``` + +## 退款 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->refund([ + 'out_trade_no' => '1623160012', + 'refund_amount' => '0.01', +]); +``` + +## 查询订单 + +```php +Pay::config($this->config); + +$order = [ + 'out_trade_no' => '1514027114', +]; +// $order = '1514027114'; + +$result = Pay::alipay()->find($order); +``` + +## 支付宝回调处理 + +```php +Pay::config($this->config); + +$result = Pay::alipay()->callback(); +``` + +## 响应支付宝回调 + +```php +Pay::config($this->config); + +return Pay::alipay()->success(); +``` diff --git a/web/docs/v3/quick-start/init.md b/web/docs/v3/quick-start/init.md new file mode 100644 index 0000000..a385f8c --- /dev/null +++ b/web/docs/v3/quick-start/init.md @@ -0,0 +1,150 @@ +# 初始化 + +初始化有两种方式,大家可根据自己的习惯选择合适的方式。 + +SDK 一旦初始化后,底层使用单例模式保存配置信息,所以,每次使用只需初始化一次即可,无需多次,后续重复初始化将不会生效 +当然,您也可以使用 `_force` 参数强制初始化覆盖原来的配置项。 + +假设有以下配置文件: + +```php +$config = [ + 'alipay' => [ + 'default' => [ + // 必填-支付宝分配的 app_id + 'app_id' => '2016082000295641', + // 必填-应用私钥 字符串或路径 + 'app_secret_cert' => 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCDRjOg5DnX+8L+rB8d2MbrQ30Z7JPM4hiDhawHSwQCQ7RlmQNpl6b/N6IrPLcPFC1uii179U5Il5xTZynfjkUyJjnHusqnmHskftLJDKkmGbSUFMAlOv+NlpUWMJ2A+VUopl+9FLyqcV+XgbaWizxU3LsTtt64v89iZ2iC16H6/6a3YcP+hDZUjiNGQx9cuwi9eJyykvcwhDkFPxeBxHbfwppsul+DYUyTCcl0Ltbga/mUechk5BksW6yPPwprYHQBXyM16Jc3q5HbNxh3660FyvUBFLuVWIBs6RtR2gZCa6b8rOtCkPQKhUKvzRMlgheOowXsWdk99GjxGQDK5W4XAgMBAAECggEAYPKnjlr+nRPBnnNfR5ugzH67FToyrU0M7ZT6xygPfdyijaXDb2ggXLupeGUOjIRKSSijDrjLZ7EQMkguFHvtfmvcoDTDFaL2zq0a3oALK6gwRGxOuzAnK1naINkmeOmqiqrUab+21emEv098mRGbLNEXGCgltCtz7SiRdo/pgIPZ1wHj4MH0b0K2bFG3xwr51EyaLXKYH4j6w9YAXXsTdvzcJ+eRE0Yq4uGPfkziqg8d0xXSEt90HmCGHKo4O2eh1w1IlBcHfK0F6vkeUAtrtAV01MU2bNoRU147vKFxjDOVBlY1nIZY/drsbiPMuAfSsodL0hJxGSYivbKTX4CWgQKBgQDd0MkF5AIPPdFC+fhWdNclePRw4gUkBwPTIUljMP4o+MhJNrHp0sEy0sr1mzYsOT4J20hsbw/qTnMKGdgy784bySf6/CC7lv2hHp0wyS3Es0DRJuN+aTyyONOKGvQqd8gvuQtuYJy+hkIoHygjvC3TKndX1v66f9vCr/7TS0QPywKBgQCXgVHERHP+CarSAEDG6bzI878/5yqyJVlUeVMG5OXdlwCl0GAAl4mDvfqweUawSVFE7qiSqy3Eaok8KHkYcoRlQmAefHg/C8t2PNFfNrANDdDB99f7UhqhXTdBA6DPyW02eKIaBcXjZ7jEXZzA41a/zxZydKgHvz4pUq1BdbU5ZQKBgHyqGCDgaavpQVAUL1df6X8dALzkuqDp9GNXxOgjo+ShFefX/pv8oCqRQBJTflnSfiSKAqU2skosdwlJRzIxhrQlFPxBcaAcl0VTcGL33mo7mIU0Bw2H1d4QhAuNZIbttSvlIyCQ2edWi54DDMswusyAhHxwz88/huJfiad1GLaLAoGASIweMVNuD5lleMWyPw2x3rAJRnpVUZTc37xw6340LBWgs8XCEsZ9jN4t6s9H8CZLiiyWABWEBufU6z+eLPy5NRvBlxeXJOlq9iVNRMCVMMsKybb6b1fzdI2EZdds69LSPyEozjkxdyE1sqH468xwv8xUPV5rD7qd83+pgwzwSJkCgYBrRV0OZmicfVJ7RqbWyneBG03r7ziA0WTcLdRWDnOujQ9orhrkm+EY2evhLEkkF6TOYv4QFBGSHfGJ0SwD7ghbCQC/8oBvNvuQiPWI8B+00LwyxXNrkFOxy7UfIUdUmLoLc1s/VdBHku+JEd0YmEY+p4sjmcRnlu4AlzLxkWUTTg==', + // 必填-应用公钥证书 路径 + 'app_public_cert_path' => '/Users/yansongda/pay/cert/appCertPublicKey_2016082000295641.crt', + // 必填-支付宝公钥证书 路径 + 'alipay_public_cert_path' => '/Users/yansongda/pay/cert/alipayCertPublicKey_RSA2.crt', + // 必填-支付宝根证书 路径 + 'alipay_root_cert_path' => '/Users/yansongda/pay/cert/alipayRootCert.crt', + 'return_url' => 'https://yansongda.cn/alipay/return', + 'notify_url' => 'https://yansongda.cn/alipay/notify', + // 选填-第三方应用授权token + 'app_auth_token' => '', + // 选填-服务商模式下的服务商 id,当 mode 为 Pay::MODE_SERVICE 时使用该参数 + 'service_provider_id' => '', + // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SANDBOX, MODE_SERVICE + 'mode' => Pay::MODE_NORMAL, + ] + ], + 'wechat' => [ + 'default' => [ + // 必填-商户号,服务商模式下为服务商商户号 + 'mch_id' => '', + // 必填-商户秘钥 + 'mch_secret_key' => '', + // 必填-商户私钥 字符串或路径 + 'mch_secret_cert' => '', + // 必填-商户公钥证书路径 + 'mch_public_cert_path' => '', + // 必填 + 'notify_url' => 'https://yansongda.cn/wechat/notify', + // 选填-公众号 的 app_id + 'mp_app_id' => '2016082000291234', + // 选填-小程序 的 app_id + 'mini_app_id' => '', + // 选填-app 的 app_id + 'app_id' => '', + // 选填-合单 app_id + 'combine_app_id' => '', + // 选填-合单商户号 + 'combine_mch_id' => '', + // 选填-服务商模式下,子公众号 的 app_id + 'sub_mp_app_id' => '', + // 选填-服务商模式下,子 app 的 app_id + 'sub_app_id' => '', + // 选填-服务商模式下,子小程序 的 app_id + 'sub_mini_app_id' => '', + // 选填-服务商模式下,子商户id + 'sub_mch_id' => '', + // 选填-微信公钥证书路径, optional,强烈建议 php-fpm 模式下配置此参数 + 'wechat_public_cert_path' => [ + '45F59D4DABF31918AFCEC556D5D2C6E376675D57' => __DIR__.'/Cert/wechatPublicKey.crt', + ], + // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE + 'mode' => Pay::MODE_NORMAL, + ] + ], + 'logger' => [ + 'enable' => false, + 'file' => './logs/alipay.log', + 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ], +]; +``` + +## 初始化方式 + +### 方式一 + +直接调用 `config` 方法初始化 + +```php +Pay::config($config); +``` + +如果需要强制初始化覆盖配置信息 + +```php +Pay::config(array_merge($config, ['_force' => true])); +``` + +### 方式二 + +在每次实际调用时顺便初始化 + +```php +Pay::alipay($config)->web($order); +``` + +如果需要强制初始化覆盖配置信息 + +```php +Pay::alipay(array_merge($config, ['_force' => true]))->web($order); +``` + +## 配置切换 + +v3.x 版本开始,支持了多租户功能,所以,不同租户有不同的配置项,如果想要在使用时切换配置项怎么办呢? + +其实很简单,传参时,加一个参数即可: `'_config' => 'default'`。 + +例如,我们想在查询支付宝支付订单时,使用另外一个租户的配置文件 + +```php +Pay::config($this->config); + +$order = [ + 'out_trade_no' => '1514027114', + '_config' => 'default', // 注意这一行 +]; + +$result = Pay::alipay()->find($order); +``` + +## 关于微信公钥证书 + +微信支付公钥证书主要用于微信支付响应消息时的验签动作。 + +例如,当请求给微信支付服务器时,微信支付接收到请求会作出响应,系统在接收到响应后,需要验证这个响应是不是微信支付服务器官方发出的,以防止欺诈,这个验证的动作,就会使用到微信公钥证书。 + +关于微信公钥证书的详细介绍可以参考[微信官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml) + +::: tip 常驻进程模式 +如果您在 Swoole 等常驻进程下使用 Pay,那您无需再配置 `wechat_public_cert_path` 参数,Pay 会自动帮你搞定一切:从微信服务器获取最新证书并自动缓存配置。 +::: + +::: warning PHP-FPM 模式 +如果您在 php-fpm 进程下使用 Pay,强烈建议您手动配置 `wechat_public_cert_path` 参数。当然,您也可以不用配置,不过,您每次支付将从微信服务器获取最新的证书并验证,这将有性能上的损耗。 +::: diff --git a/web/docs/v3/quick-start/install.md b/web/docs/v3/quick-start/install.md new file mode 100644 index 0000000..83696c6 --- /dev/null +++ b/web/docs/v3/quick-start/install.md @@ -0,0 +1,104 @@ +# 安装 + +## 运行环境 + +- PHP 7.3+ (v3.1.0 开始需 7.4+) +- composer + +## 安装总结 + +对于绝大多数用户而言,您只需要记住以下两个原则即可: + +### hyperf/laravel 用户 + +```shell +composer require yansongda/pay:~3.1.0 -vvv +composer require guzzlehttp/guzzle:^7.0 # 默认情况下,此包框架已自带,无需额外安装 +``` + +### 其它框架/无框架 用户 + +```shell +composer require yansongda/pay:~3.1.0 -vvv +composer require guzzlehttp/guzzle:^7.0 +composer require php-di/php-di -W +``` + +## 详细安装介绍 + +```shell +composer require yansongda/pay:~3.1.0 -vvv +``` + +由于 `yansongda/pay` 支持 PSR2、PSR3、PSR4、PSR7、PSR11、PSR14、PSR18 等各项标准,因此这里额外介绍下 PSR-11、PSR-18 的安装与使用。 + +### 关于 Container(PSR-11) + +::: tip +如果您看不懂这部分内容: + +1、hyperf/laravel 用户直接忽略此部分内容; + +2、其它用户(包括 thinkphp 用户)在安装完 `Pay` 后直接无脑 `composer require php-di/php-di` 即可 +::: + +#### hyperf/laravel 用户 + +`Pay` 会自动复用框架内的 Container, 无需您任何额外操作。 + +#### 其它框架/无框架 用户 + +如果您不想操心那么多,SDK 自带了一套开箱即用的 Container,但仍然需要手动安装 php-di: + +```shell +composer require php-di/php-di +``` + + +如果您所使用的框架内有符合 `PSR-11` 的 `Container`,您需要在初始化 **之前**(即,调用 `Pay::config()` 方法之前)执行以下代码即可复用现有的 `Container`: + +```php +use Yansongda\Pay\Pay; +use Yansongda\Pay\Contract\HttpClientInterface; + +// $container = 您现有的 container + +// 方法一: +Pay::setContainer($container); +Pay::config($config); + +// 方法二: +Pay::config($config, function () use ($container) { + return $container; +}); +``` + +### 关于 Guzzlehttp (PSR-18) + +::: tip +如果您看不懂这部分内容: + +在安装完 `Pay` 后直接无脑 `composer require guzzlehttp/guzzle:^7.0` 即可 +::: + +#### 使用默认的 Client + +SDK 自带了一套开箱即用的 HTTP 客户端,但仍然需要手动安装 Guzzlehttp: + +```shell +composer require guzzlehttp/guzzle:^7.0 +``` + +#### 现有框架有 PSR-18 的 Client + +如果您所使用的框架内有符合 `PSR-18` 的 `HTTP Client`,您需要在初始化 **之后**(即,调用 `Pay::config()` 方法后)执行以下代码即可复用现有的 `Client`: + +```php +use Yansongda\Pay\Pay; +use Yansongda\Pay\Contract\HttpClientInterface; + +// $client = 您现有的 http client + +Pay::config($config); +Pay::set(HttpClientInterface::class, $client); +``` diff --git a/web/docs/v3/quick-start/return-format.md b/web/docs/v3/quick-start/return-format.md new file mode 100644 index 0000000..c1c1a33 --- /dev/null +++ b/web/docs/v3/quick-start/return-format.md @@ -0,0 +1,62 @@ +# 返回格式 + +通过 `yansongda/pay` 调用任何方法最终只可能返回三种格式 + +- `\Psr\Http\Message\MessageInterface` +- `\Yansongda\Supports\Collection` +- `array` + +其中 `\Psr\Http\Message\MessageInterface` 最终 实例/接口 为 + +- `\GuzzleHttp\Psr7\Response` + +:::tip +至于最终返回的到底是什么类型,和不同的方法而定 +::: + +## MessageInterface + +### `\GuzzleHttp\Psr7\Response` + +支付宝中 + +- `app()` APP 支付 +- `web()` web 支付 +- `wap()` wap 支付 +- `success()` 响应回调 + +微信中 + +- `success()` 响应回调 + +均返回此类,在支持 PSR7 的框架中均可直接返回响应请求 + +:::tip Laravel 框架 +laravel 框架中,自行安装 `symfony/psr-http-message-bridge` 即可支持返回相关响应数据 +::: + +:::warning ThinkPHP 框架 +ThinkPHP 框架在 [https://github.com/top-think/framework/pull/2614](https://github.com/top-think/framework/pull/2614) 之后才支持 PSR7 规范,因此,之前的版本需要参考此 PR 自行解包进行处理返回数据 +::: + +## Collection + +默认情况下,支付宝、微信所有 API 调用场景下绝大多数方法最终都返回的是 `Collection` 实例。 +例如常用的「退款」「转账」「小程序支付」等。 + +`Collection` 类提供了常用的快捷方法,具体 API 可参考源代码 [yansongda/supports](https://github.com/yansongda/supports) + +## array + +API 调用场景下的返回类型,`array` 和 `Collection` 是可以自定义的,默认情况下均返回 `Collection` 实例。 + +如果想返回 array 类型的数据,只需要 + +```php +use Yansongda\Pay\Contract\ParserInterface; +use Yansongda\Pay\Parser\ArrayParser; + +Pay::set(ParserInterface::class, ArrayParser::class); +``` + +是不是很简单方便? diff --git a/web/docs/v3/quick-start/wechat.md b/web/docs/v3/quick-start/wechat.md new file mode 100644 index 0000000..a957866 --- /dev/null +++ b/web/docs/v3/quick-start/wechat.md @@ -0,0 +1,132 @@ +# 微信快速入门 + +在初始化完毕后,就可以直接方便的享受 `yansongda/pay` 带来的便利了。 + +:::tip +`yansongda/pay` v3.x 版本直接支持 微信支付v3 版本,关于微信支付 v2/v3 版本区别,请参考[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml) +::: + +## 公众号支付 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], + 'payer' => [ + 'openid' => 'onkVf1FjWS5SBxxxxxxxx', + ], +]; + +$result = Pay::wechat()->mp($order); +// 返回 Collection 实例。包含了调用 JSAPI 的所有参数,如appId,timeStamp,nonceStr,package,signType,paySign 等; +// 可直接通过 $result->appId, $result->timeStamp 获取相关值。 +// 后续调用不在本文档讨论范围内,请自行参考官方文档。 +``` + +## 手机网站支付 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], + 'scene_info' => [ + 'payer_client_ip' => '1.2.4.8', + 'h5_info' => [ + 'type' => 'Wap', + ] + ], +]; + +return Pay::wechat()->wap($order); +``` + +## APP 支付 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], +]; + +// 将返回 json 格式,供后续 APP 调用,调用方式不在本文档讨论范围内,请参考官方文档。 +return Pay::wechat()->app($order); +``` + +## 扫码支付 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], +]; + +$result = Pay::wechat()->scan($order); +// 二维码内容: $qr = $result->code_url; +``` + +## 小程序 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + 'currency' => 'CNY', + ], + 'payer' => [ + 'openid' => '123fsdf234', + ] +]; + +$result = Pay::wechat()->mini($order); +// 返回 Collection 实例。包含了调用 JSAPI 的所有参数,如appId,timeStamp,nonceStr,package,signType,paySign 等; +// 可直接通过 $result->appId, $result->timeStamp 获取相关值。 +// 后续调用不在本文档讨论范围内,请自行参考官方文档。 +``` + +## 刷卡支付 + +:::warning +微信支付 v3 版 api 并不支持刷卡支付,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: + +## 账户转账 + +:::warning +微信支付 v3 版 api 并不支持转账,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: + +## 普通红包 + +:::warning +微信支付 v3 版 api 并不支红包,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: + +## 裂变红包 + +:::warning +微信支付 v3 版 api 并不支持红包,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: diff --git a/web/docs/v3/sidebar.js b/web/docs/v3/sidebar.js new file mode 100644 index 0000000..6dfbef0 --- /dev/null +++ b/web/docs/v3/sidebar.js @@ -0,0 +1,79 @@ +exports = module.exports = [ + { + text: '概述', + collapsible: true, + items: [ + { text: '线上交流', link: '/docs/v3/overview/communication'}, + { text: '捐赠', link: '/docs/v3/overview/donate'}, + { text: '参与开发', link: '/docs/v3/overview/contribute'}, + { text: '更新记录', link: '/docs/v3/overview/versions'}, + { text: '商业与企业服务', link: '/docs/v3/overview/business'} + ] + }, + { + text: '快速入门', + collapsible: true, + items: [ + { text: '安装', link: '/docs/v3/quick-start/install'}, + { text: '初始化', link: '/docs/v3/quick-start/init'}, + { text: '支付宝', link: '/docs/v3/quick-start/alipay'}, + { text: '微信', link: '/docs/v3/quick-start/wechat'}, + { text: '返回格式', link: '/docs/v3/quick-start/return-format'} + ] + }, + { + text: '支付宝', + collapsible: true, + items: [ + { text: '支付', link: '/docs/v3/alipay/pay'}, + { text: '查询', link: '/docs/v3/alipay/find'}, + { text: '退款', link: '/docs/v3/alipay/refund'}, + { text: '关闭', link: '/docs/v3/alipay/close'}, + { text: '取消', link: '/docs/v3/alipay/cancel'}, + { text: '接收回调', link: '/docs/v3/alipay/callback'}, + { text: '确认回调', link: '/docs/v3/alipay/response'}, + { text: '更多便捷插件', link: '/docs/v3/alipay/more'}, + ] + }, + { + text: '微信', + collapsible: true, + items: [ + { text: '支付', link: '/docs/v3/wechat/pay'}, + { text: '查询', link: '/docs/v3/wechat/find'}, + { text: '退款', link: '/docs/v3/wechat/refund'}, + { text: '关闭', link: '/docs/v3/wechat/close'}, + { text: '取消', link: '/docs/v3/wechat/cancel'}, + { text: '接收回调', link: '/docs/v3/wechat/callback'}, + { text: '确认回调', link: '/docs/v3/wechat/response'}, + { text: '更多便捷插件', link: '/docs/v3/wechat/more'} + ] + }, + { + text: '核心架构', + collapsible: true, + items: [ + { text: '🚀 Rocket', link: '/docs/v3/kernel/rocket'}, + { text: '🧪 Pipeline', link: '/docs/v3/kernel/pipeline'}, + { text: '🔌 Plugin', link: '/docs/v3/kernel/plugin'}, + { text: '💤 Shortcut', link: '/docs/v3/kernel/shortcut'} + ] + }, + { + text: '其它', + collapsible: true, + items: [ + { text: '事件', link: '/docs/v3/others/event'}, + { text: '日志', link: '/docs/v3/others/logger'}, + { text: 'FAQ', link: '/docs/v3/others/faq'}, + ] + }, + { + text: '升级指南', + collapsible: true, + items: [ + { text: 'v3.1 升级指南', link: '/docs/v3/upgrade/v3.1'}, + { text: 'v3.0 升级指南', link: '/docs/v3/upgrade/v3.0'} + ] + } +] diff --git a/web/docs/v3/upgrade/v3.0.md b/web/docs/v3/upgrade/v3.0.md new file mode 100644 index 0000000..49045e1 --- /dev/null +++ b/web/docs/v3/upgrade/v3.0.md @@ -0,0 +1,28 @@ +# v3.0 升级指南 + +## 配置文件调整 + +v3.0 版本由于支持了多租户,所以配置文件有所调整。 + +请参考 [初始化](/docs/v3/quick-start/init.md) 调整您的配置文件。 + +## 初始化方式调整(非必须,但推荐) + +此项非必须,但是强烈推荐您更改调整 + +同样参考 [初始化](/docs/v3/quick-start/init.md) 的 **方式一** 调整您的配置初始化。 + +## 更改版本号 + +将 composer.json 的 yansongda/pay 版本号更改为 `~3.0.0`,随后 `composer update` 即可。 + +## break-change + +见 [发布说明](/docs/v3/overview/versions.md) + +## 微信API问题 + +v3.0 版本开始,Pay 使用微信 v3.0 API,因此, v3.0 API 中不支持的操作,Pay v3.0 也不会支持,可以参考微信支付官方文档或 [https://github.com/yansongda/pay/issues/473](https://github.com/yansongda/pay/issues/473) + +如果你强依赖微信支付中 v2 版本的API,建议保持 Pay v2 版本不做升级操作 + diff --git a/web/docs/v3/upgrade/v3.1.md b/web/docs/v3/upgrade/v3.1.md new file mode 100644 index 0000000..36d464c --- /dev/null +++ b/web/docs/v3/upgrade/v3.1.md @@ -0,0 +1,25 @@ +# v3.1 升级指南 + +## PHP 最低版本 + +v3.1 将 PHP 最低版本由 7.3 提高到了 7.4 + +## 更改版本号 + +将 composer.json 的 yansongda/pay 版本号更改为 `~3.1.0`,随后 `composer update` 即可。 + +## PSR-11 和 PSR-18 + +### hyperf/laravel 用户 + +无需任何操作 + +### 其它框架/无框架 用户 + +如果您使用其它框架 或 未使用任何框架,需额外执行以下命令: + +```shell +composer require php-di/php-di +``` + +关于此部分的说明,可以额外查阅 [安装](/docs/v3/quick-start/install.md) 部分 diff --git a/web/docs/v3/wechat/callback.md b/web/docs/v3/wechat/callback.md new file mode 100644 index 0000000..f5092c6 --- /dev/null +++ b/web/docs/v3/wechat/callback.md @@ -0,0 +1,40 @@ +# 接收回调 + +| 方法名 | 参数 | 返回值 | +|:--------:|:------------------------------:|:----------:| +| callback | 无/array/ServerRequestInterface | Collection | + +## 例子 + +```php +Pay::config($this->config); + +// 是的,你没有看错,就是这么简单! +$result = Pay::wechat()->callback(); +``` + +## 参数 + +### 第一个参数 + +#### `null` + +如果您没有传参,则 `yansongda/pay` 会自动识别微信的回调请求并进行验签解密处理,通过 `Collection` 实例返回微信的处理参数 + +:::warning +建议仅在 php-fpm 下使用,swoole 方式请使用 `ServerRequestInterface` 参数传递方式 +::: + +#### `ServerRequestInterface` + +推荐在 swoole 环境下传递此参数,传递此参数后, yansongda/pay 会自动进行后续处理 + +#### `array` + +也可以自行解析请求参数,传递一个 array 会自动进行后续处理 + +### 第二个参数 + +第二个参数主要是传递相关自定义变量的,类似于 `web()` 中的 `_config` / `_method` 等参数。 + +例如,如果你想在回调的时候使用非默认配置,则可以 `Pay::wechat()->callback(null, ['_config' => 'yansongda'])` 切换为 `yansongda` 这个租户的配置信息。 diff --git a/web/docs/v3/wechat/cancel.md b/web/docs/v3/wechat/cancel.md new file mode 100644 index 0000000..512411d --- /dev/null +++ b/web/docs/v3/wechat/cancel.md @@ -0,0 +1,9 @@ +# 取消订单 + +:::danger +微信官方无此 API,请调用 close 关闭订单。 +::: + +## 异常 + +Yansongda\Pay\Exceptions\InvalidParamsException diff --git a/web/docs/v3/wechat/close.md b/web/docs/v3/wechat/close.md new file mode 100644 index 0000000..929aa41 --- /dev/null +++ b/web/docs/v3/wechat/close.md @@ -0,0 +1,24 @@ +# 关闭订单 + +| 方法名 | 参数 | 返回值 | +|:-----:|:-------------------:|:----------:| +| close | string/array $order | Collection | + +## 例子 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => '1514027114', +]; + +// $order = '1514027114'; + +$result = Pay::wechat()->close($order); +``` + + +## 订单配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml),查看「请求参数」一栏。 diff --git a/web/docs/v3/wechat/find.md b/web/docs/v3/wechat/find.md new file mode 100644 index 0000000..38d0b07 --- /dev/null +++ b/web/docs/v3/wechat/find.md @@ -0,0 +1,61 @@ +# 查询订单 + +| 方法名 | 参数 | 返回值 | +|:----:|:-------------------:|:----------:| +| find | string/array $order | Collection | + +## 查询支付订单 + +```php +Pay::config($config); + +$order = [ + 'transaction_id' => '1217752501201407033233368018', +]; +// $order = '1217752501201407033233368018'; + +$result = Pay::wechat()->find($order); +``` + +## 查询退款订单 + +```php +Pay::config($config); + +$order = [ + 'transaction_id' => '1217752501201407033233368018', + '_type' => 'refund' +]; + +$result = Pay::wechat()->find($order); +``` + +## 查询合单支付订单 + +```php +Pay::config($config); + +$order = [ + 'combine_out_trade_no' => '1217752501201407033233368018', +]; +//$order = [ +// 'transaction_id' => '1217752501201407033233368018', +// '_type' => 'combine', +//]; + +$result = Pay::wechat()->find($order); +``` + +## 订单配置参数 + +### 查询支付订单 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml),查看「请求参数」一栏。 + +### 查询退款订单 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml),查看「请求参数」一栏。 + +### 查询合单支付订单 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_11.shtml),查看「请求参数」一栏。 diff --git a/web/docs/v3/wechat/more.md b/web/docs/v3/wechat/more.md new file mode 100644 index 0000000..a94a80e --- /dev/null +++ b/web/docs/v3/wechat/more.md @@ -0,0 +1,244 @@ +# 更多方便的插件 + +得益于 yansongda/pay 的基础架构和良好的插件机制, +您可以自有的使用任何内置插件和自定义插件调用支付宝的任何 API。 + +诸如签名、API调用、解密、验签、解包等基础插件已经内置在 Pay 中, +您可以使用 `Pay::wechat()->mergeCommonPlugins(array $plugins)` 来获取调用 API 所必须的常用插件 + +首先,查找你想使用的插件,然后 + +```php +Pay::config($config); + +$params = [ + 'transaction_id' => '1217752501201407033233368018', +]; + +$allPlugins = Pay::wechat()->mergeCommonPlugins([QueryPlugin::class]); + +$result = Pay::wechat()->pay($allPlugins, $params); +``` + +关于插件的详细介绍,如果您感兴趣,可以参考 [这篇说明文档](/docs/v3/kernel/plugin.md) + +## 资金应用-分账 + +### 添加分账接收方 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\AddReceiverPlugin` + +### 请求分账 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\CreatePlugin` + +### 删除分账接收方 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\DeleteReceiverPlugin` + +### 查询剩余待分金额 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\QueryAmountsPlugin` + +### 查询分账结果 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\QueryPlugin` + +### 查询分账回退结果 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\QueryReturnPlugin` + +### 请求分账回退 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\ReturnPlugin` + +### 解冻剩余资金 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\UnfreezePlugin` + +### 查询最大分账比例 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\QueryMerchantConfigsPlugin` + +### 下载账单 + +- `Yansongda\Pay\Plugin\Wechat\Fund\Profitsharing\DownloadBillPlugin` + +## 资金应用-转账到零钱 + +### 发起批量转账 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\CreatePlugin` + +### 微信批次单号查询批次单 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryBatchIdPlugin` + +### 微信明细单号查询明细单 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryBatchDetailIdPlugin` + +### 商家批次单号查询批次单 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryOutBatchNoPlugin` + +### 商家明细单号查询明细单 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryOutBatchDetailNoPlugin` + +### 转账电子回单申请受理 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\CreateBillReceiptPlugin` + +### 查询转账电子回单 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryBillReceiptPlugin` + +### 转账明细电子回单受理 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\CreateDetailReceiptPlugin` + +### 查询转账明细电子回单受理结果 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryBillReceiptPlugin` + +### 下载电子回单 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\DownloadReceiptPlugin` + +### 查询转账明细电子回单受理结果 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryDetailReceiptPlugin` + +### 查询账户实时余额 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Balance\QueryPlugin` + +### 查询账户日终余额 + +- `\Yansongda\Pay\Plugin\Wechat\Fund\Balance\QueryDayEndPlugin` + +## 营销工具-代金券 + +### 创建代金券批次 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\CreatePlugin` + +### 暂停代金券批次 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\PausePlugin` + +### 根据商户号查用户的券 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryCouponDetailPlugin` + +### 查询批次详情 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryStockDetailPlugin` + +### 查询代金券可用单品 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryStockItemsPlugin` + +### 查询代金券可用商户 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryStockMerchantsPlugin` + +### 下载批次退款明细 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryStockRefundFlowPlugin` + +### 条件查询批次列表 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryStocksPlugin` + +### 下载批次核销明细 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryStockUseFlowPlugin` + +### 根据商户号查用户的券 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\QueryUserCouponsPlugin` + +### 重启代金券批次 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\RestartPlugin` + +### 发放代金券批次 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\SendPlugin` + +### 设置消息通知地址API + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\SetCallbackPlugin` + +### 激活代金券批次 + +- `Yansongda\Pay\Plugin\Wechat\Marketing\Coupon\StartPlugin` + +## 风险合规-消费者投诉 + +### 查询投诉单列表 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\QueryComplaintsPlugin` + +### 查询投诉单详情 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\QueryComplaintDetailPlugin` + +### 查询投诉协商历史 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\QueryComplaintNegotiationPlugin` + +### 创建投诉通知回调地址 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\SetCallbackPlugin` + +### 查询投诉通知回调地址 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\QueryCallbackPlugin` + +### 更新投诉通知回调地址 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\UpdateCallbackPlugin` + +### 删除投诉通知回调地址 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\DeleteCallbackPlugin` + +### 回复用户 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\ResponseComplaintPlugin` + +### 反馈处理完成 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\CompleteComplaintPlugin` + +### 更新退款进度 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\UpdateRefundPlugin` + +### 图片下载 + +- `Yansongda\Pay\Plugin\Wechat\Risk\Complaints\DownloadMediaPlugin` + + +## 服务商-行业方案 + +### 电商收付通(退款) + +#### 申请退款 + +- `Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund\ApplyPlugin` + +#### 查询退款 + +- `Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund\FindPlugin` + +#### 查询垫付回补结果 + +- `Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund\FindReturnAdvancePlugin` + +#### 垫付退款回补 + +- `Yansongda\Pay\Plugin\Wechat\Ecommerce\Refund\ReturnAdvancePlugin` diff --git a/web/docs/v3/wechat/pay.md b/web/docs/v3/wechat/pay.md new file mode 100644 index 0000000..0f4386a --- /dev/null +++ b/web/docs/v3/wechat/pay.md @@ -0,0 +1,249 @@ +# 支付 + +微信支付目前直接内置支持 7 种快捷方式支付方法,对应的支付 method 如下: + +| method | 说明 | 参数 | 返回值 | +|:------:|:------:|:------------:|:----------:| +| mp | 公众号支付 | array $order | Collection | +| wap | 手机网站支付 | array $order | Collection | +| app | APP 支付 | array $order | Collection | +| scan | 扫码支付 | array $order | Collection | +| mini | 小程序支付 | array $order | Collection | + +## 公众号支付 + +### 例子 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], + 'payer' => [ + 'openid' => 'onkVf1FjWS5SBxxxxxxxx', + ], +]; + +$result = Pay::wechat()->mp($order); +// 返回 Collection 实例。包含了调用 JSAPI 的所有参数,如appId,timeStamp,nonceStr,package,signType,paySign 等; +// 可直接通过 $result->appId, $result->timeStamp 获取相关值。 +// 后续调用不在本文档讨论范围内,请自行参考官方文档。 +``` + +:::warning 调起微信支付 timeStamp 参数问题 +[微信支付](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml) 和 [微信](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#58) 两个文档所需要的参数不一致,微信支付中是 timeStamp, 微信调起的参数是 timestamp,需要自行处理。 +::: + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`appid`,`sign` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml),查看「请求参数」一栏。 + + +## 手机网站支付 + +### 例子 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], + 'scene_info' => [ + 'payer_client_ip' => '1.2.4.8', + 'h5_info' => [ + 'type' => 'Wap', + ] + ], +]; + +return Pay::wechat()->wap($order); +// $result->h5_url; +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml),查看「请求参数」一栏。 + +### 调用支付 + +后续调起支付不再本文档讨论范围内,请参考[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_4.shtml) + +### 其它 + +#### 使用小程序的 app_id 关联 h5 支付 + +默认情况下,H5 支付所使用的 appid 是微信公众号的 appid,即配置文件中的 mp_app_id 参数,如果想使用关联的小程序的 appid,则只需要在调用参数中增加 `['_type' => 'mini']` 即可,例如: + +```php +$order = [ + '_type' => 'mini', // 注意这一行 + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], + 'scene_info' => [ + 'payer_client_ip' => '1.2.4.8', + 'h5_info' => [ + 'type' => 'Wap', + ] + ], +]; +``` + +## APP 支付 + +### 例子 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], +]; + +// 将返回 Collection 实例,供后续 APP 调用,调用方式不在本文档讨论范围内,请参考官方文档。 +return Pay::wechat()->app($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml),查看「请求参数」一栏。 + +### 调用支付 + +后续调起支付不再本文档讨论范围内,请参考[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml) + +## 扫码支付 + +### 例子 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + ], +]; + +$result = Pay::wechat()->scan($order); +// 二维码内容: $qr = $result->code_url; +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml),查看「请求参数」一栏。 + +### 调用支付 + +后续调起支付不再本文档讨论范围内,请参考[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_4.shtml) + +## 小程序 + +### 例子 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => time().'', + 'description' => 'subject-测试', + 'amount' => [ + 'total' => 1, + 'currency' => 'CNY', + ], + 'payer' => [ + 'openid' => '123fsdf234', + ] +]; + +$result = Pay::wechat()->mini($order); +// 返回 Collection 实例。包含了调用 JSAPI 的所有参数,如appId,timeStamp,nonceStr,package,signType,paySign 等; +// 可直接通过 $result->appId, $result->timeStamp 获取相关值。 +// 后续调用不在本文档讨论范围内,请自行参考官方文档。 +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`trade_type`,`appid`,`sign` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml),查看「请求参数」一栏。 + +### 调用支付 + +后续调起支付不再本文档讨论范围内,请参考[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml) + +## 账户转账 + +### 例子 + +```php +Pay::config($config); + +$order = [ + 'out_batch_no' => time().'', + 'batch_name' => 'subject-测试', + 'batch_remark' => 'test', + 'total_amount' => 1, + 'total_num' => 1, + 'transfer_detail_list' => [ + [ + 'out_detail_no' => time().'-1', + 'transfer_amount' => 1, + 'transfer_remark' => 'test', + 'openid' => 'MYE42l80oelYMDE34nYD456Xoy', + // 'user_name' => '闫嵩达' // 明文传参即可,sdk 会自动加密 + ], + ], +]; + +$result = Pay::wechat()->transfer($order); +``` + +### 订单配置参数 + +**所有订单配置中,客观参数均不用配置,扩展包已经为大家自动处理了**,比如,`appid`,`sign` 等参数,大家只需传入订单类主观参数即可。 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_1.shtml),查看「请求参数」一节。 + + +## 刷卡支付 + +:::warning +微信支付 v3 版 api 并不支持刷卡支付,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: + +## 普通红包 + +:::warning +微信支付 v3 版 api 并不支红包,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: + +## 裂变红包 + +:::warning +微信支付 v3 版 api 并不支持红包,后续将接入微信支付 v2 版 API,敬请期待。如果确实有此需求,可以使用 [Pay 的 v2 版](/docs/v2/wechat/) +::: diff --git a/web/docs/v3/wechat/refund.md b/web/docs/v3/wechat/refund.md new file mode 100644 index 0000000..36e4a11 --- /dev/null +++ b/web/docs/v3/wechat/refund.md @@ -0,0 +1,28 @@ +# 退款 + +| 方法名 | 参数 | 返回值 | +|:------:|:------------:|:----------:| +| refund | array $order | Collection | + +## 退款操作 + +```php +Pay::config($config); + +$order = [ + 'out_trade_no' => '1514192025', + 'out_refund_no' => time(), + 'amount' => [ + 'refund' => 1, + 'total' => 1, + 'currency' => 'CNY', + ], +]; + +$result = Pay::wechat()->refund($order); +``` + +## 订单配置参数 + +所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考[这里](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml),查看「请求参数」一栏。 + diff --git a/web/docs/v3/wechat/response.md b/web/docs/v3/wechat/response.md new file mode 100644 index 0000000..dc69abb --- /dev/null +++ b/web/docs/v3/wechat/response.md @@ -0,0 +1,19 @@ +# 确认回调 + +| 方法名 | 参数 | 返回值 | +|:-------:|:---:|:--------:| +| success | 无 | Response | + +## 例子 + +```php +Pay::config($config); + +// $result = Pay::wechat()->callback(); + +return Pay::wechat()->success(); +``` + +## 订单配置参数 + +无 diff --git a/web/index.md b/web/index.md new file mode 100644 index 0000000..bdc2cbc --- /dev/null +++ b/web/index.md @@ -0,0 +1,18 @@ +--- +layout: home +title: Pay - 让支付开发更简单 + +features: + - title: 简洁至上 + details: 让我们用最少的代码写最复杂的功能,把更多时间花在游戏上。 + - title: 集成最新API + details: 集成支付宝、微信最新API,与时俱进不用担心老代码没人维护了。 + - title: 统一标准 + details: 符合最新 PSR 标准,方便你集成到各种其他框架中,无惧束缚。 + - title: 丰富的事件系统 + details: 可以 hook 到绝大部分场景,自己的代码自己掌握。 + - title: 命名不那么乱七八糟 + details: 命名统一,终于可以不用记那些烂七八糟的名称了。 + - title: 文件结构清晰 + details: 文件结构清晰,架构明了,欢迎 PR。 +--- diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..791673a --- /dev/null +++ b/web/package.json @@ -0,0 +1,13 @@ +{ + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:serve": "vitepress serve" + }, + "devDependencies": { + "@types/node": "^18.7.8", + "sass": "^1.54.5", + "vitepress": "^1.0.0-alpha.9", + "vue": "^3.2.37" + } +} diff --git a/web/public/images/icon.png b/web/public/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ef641542ad2680e6fbe0789d309f868862486d82 GIT binary patch literal 21145 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 literal 0 HcmV?d00001 diff --git a/web/public/images/jetbrains.png b/web/public/images/jetbrains.png new file mode 100644 index 0000000000000000000000000000000000000000..d79f6a6a897f025a06fdffd705edc381fd5f003b GIT binary patch literal 2687 zcmZ`*2{=^i8$ZaTF*4M(WtkcKHlu49%Zy}f*o9{(isnzVCOQ=OjDWSqg!rzyJUU z;jPS^xGR3o1VG&Td8*(T0Pt9la5x7%4hM6fko`$PL;z69^d%5P@fs@K-rfX4cfYzS zm=fW1`Er^QA^I7sAJ)U_VfC@DUGwyul7LR}xwHc%j%{`4AZf6z8f5oS5W1bR|&Req(GqMEz z0D>AFXgB6{JtR5{@tXu{__MS{WBs*SfSRx@=97WU5eLTcnBk{`U6yctR`-(^?eQ@T zJV^20cZ#rA2CX-0RJ5@rW^E!8*7v|gkv;!11Y3YC5+RTqT1j_zF7QeJnC2rbP<^!w z0_kipIWCmo_9MX8u|kGG;ygrr-U!D|hC2UcgErO=H={5AreEVXUQ~Bdwc?y z>Nk>$8_mtu#?Y4>jP&*+`w)@z;Ltq?fT0_5-C!ck8%7Tf3ZWX(v4~F@hFpJd8-;*< zN}-*@BHU~pU^p^`2-8DqA+-?3U>FRBq4@b5I+ugT5Oq(+9&yy==DRQZ2M{>@`Xr20}wp)?XX1h&WP?L!WuVG)Qu zp)c1z?@6SSzAA-KKbOTV5VaRU9Yt!PzHoD)n7v&?2NInan9*bz z^%rb0qbO=)$Eos6vl^?+idXg5Ki~$?+qVPMo*z5diq__p6^)-3#VUwP`fo5LAi0b1 zhHkCLNlys%X2Lz@iN9kNKpl!oiL~Wcx=_=*=iW{C3 z>X!-~(R1sCpkFgh+U=$diAo2%*En`tOcTSX?fTm1(cn_A#!!zQPGToK_91!Fm^I$h z6or!=@(4F=*9BGKvz6Lvh`qdmE6byG6*-(9iwoM4^$OCFEf2~%J1xFb7ta0w_&=36 zQ#Uq#?PL+`N_bKMa?%FebYK2nUERrvDr}(vwR7cTe8tds#iK`Jjg5^2&el3JH+TOb zC7T#!`yvx}cS#)`9cX%+G-$tbqSSWrzFjzsw`ms&<>{iimlZ6prSZ2$alln)n&al@ z{pq_q&E>t{jSdX()jz6z_>h$(EY}x~Q>Lv>8JwRn8)={_=RBsrM+8kY<*ltQJntB- zBjdY@F+ZKGQaU}14twLGqNWxXxjNrAQtf-Qx%srF%t#WNJmGNRI`kEBCcJWICo0zH z#(lRZPp$8o%EC>9FYIjRg*Rr%9ZTF672R~~?(2griX%M*u2wH!|KUdsk&zb7_4V~+ z->&pzenGK)Z`zEP)F~0EC59nm`YIY4X@br>dD;Qx5!UjS>7^gz;&`oWZ51!BExwGU zN*Ev#Dm?1icQ}8^BY=a_`T&<~+W%@!4*&Ba?1KiJ9zq#wJzs*$kXY*}M80!)x0F|X zw&SP9MnmXhVy{@B<-yGu;T)rg6{g&1$`O1c@21l69Bs>Auc|uJEx zdUA_aOGuEPe_kwi)_KEgAzlh!SXfxI9lf+PP!_tobFsoHU9Tw=8~J{Q|M!7`ft;ob zo0Ro^j*)ANA{Bz?hVJu34je9a(T`Y>?#tIz*YmVfA4E=|Tlh6qRb$iB(?=*01gs4^ z>yM!d=K=F{iTa4QMU-jxfYC?67!3u~=|&v_R;T_fj}TkR5DbGaskZW`DeuK+S(KM zPd|3eNlhJ`n?uTfJWStMDOAhA6)89HsWQfB+GWF;@|JYn@^o%#sSg64r;sOG_%EEA z{vmna?Pu>i?a>Jm6n46=`Bs*_$#4~A3`-AUZJutR%%z+v`MEG#+1}}L<)PIda*>(M z_4WEkOSwni*}!>(ThG|2lvJvD@BqgdCPoWnArlP3t;;%+g!jYXT8Cb} zEU{ctqBY#zp6ks`hEH>@8-_hQC9^e)EMF0~tX|7Og*UP??#>;hb$r@M7Fs+6JqN{8bwb&moxwR>>@xOL8y~ni za`g_2@2=f`VOdD6s`&Y3v0Ri_I+k}TU%}`W5(8y-sJhNddH;6#@R`;qnMlYB`2(?z zzNhA87IFeNhblaDaw9d89tIAo$;dwI%8-)_q`tgm9$TNIeiB4mebNt~zPPZkuz%*I zQfr^UW}Fsb7~CS>+tYJkJjp+%A<^%mAdGC%nUFoE2t;SUpTziU95c!@J}sZ|Qm-#^ z``S@ola-j+9rm1{Refm*VW^{*DrYxsK%HleHBhGq%Isf^j|<@^Dy^-`vFfF)!tJLi ziH8~2wo@e?hp`H#(_$C}^bt1QDxqDIZWW?8`<^Lq$>SY!EU5c6Tm)P<1#kGB*@Z(K z4r#+t9>6n_%v8qWl4kvJvcky@_58v;{fQV^(VL-yvRMT}k(mEMSRK_i=oO^;MjXuj zwg8TBrq@WH{fXd954jV35%9V=Q1O-Ge zmGaCq8k78Deey91*_X{Xm>Lc?*-3t))r+!O{3#?h(*_~N@%r-1aEF< JR%uFz`x~^9~9Zfa!;QXEWun%m#1-QT7w%;1loW&WJo9}*+_du3ezf$BB(Ys5OlqUnCW2gUo zFBKlge4CRk>!~D31CEZ9jVy;(l`}^>_%2agG#7;SuY3wUcq#ZX|4#jfzGvbBjUtR8 zqJxdKUV9uCGIo%P^TU!Vj0>U9?p9D6nEMB^v zCV232mgrKr{?}!gQeuyNR~LvY&W9^4QlOVa2hHyzi#Ne(U_W8u59l-c;8SoKtO~I^ z+`+~7G>CYLQ9WLrr}#06jy&sQ3NSGk4&t%b`_GHyCjr&tH@&}Gvik1D<$UES*1yfO zaRHx*PKV#yaM1N(tG^X2pV$-VN&ApO9HV~`4LrWa{=!EpO@@3RmSaaQn)Xl))*zBs(I4Jm`aj322Fdwq7vM9MoWM^A~HD<&N6 zx`i#haBC28$51z=BwT4Yir+GJnDkh!j?tE2o4k_)R@pj_eTMZ!-lkV?lNKe?O)EtZ zTcOspH}ya4Y1Y--BtkupgzCi;dm3h9Ki=0%rzHTX*FhABWEt3!HXqaQSc8?#4iwcX z2$OmN=yvzwf!;zg+>(jF>ilu^N{`q)eI*H++|Qq=2gu0TubnNppQU6gMU0 zeKdX*Z97`sGB6D^@z!Ii zIf;Vf=OjAkPjM1ByTgG-?gg@1qTQVX`#d@nGloV$4Y^A*mjETQ`(lVQJVgGI>7@Cv z9K%yU`vtpb99}lFE}_@Z5}p=H{_`Fmrvs&( zZUeTFzJRD>VhbBbhncL7cC?h7rwXsc>17dE>2WqtBLg@N$2Y&5dZdm};UulWUPp2e zF3p=mzG4!cP)WQm&q_Wu)TtR4^D(E7K(J}&Z= z7(9ZyjXSZ2{3_M$0jW+X}h!85I`6>y6wJ+&b0;G#`lo(QpD{jdhisGnE(+8BAPJb&uxlYq?AT;C+rpRj37M4r)gLbFag0*eq&-_q=p>Ps(a7MINeIrXHfnoyvB};CrA)zS{_jD`WCX~D{&F}TIxtw;9%&ePznvb#^O%Yu z=uui}Yg%_{7`UJ?yzw0uC6~<)w|{aI;GX}fXs^v2_Mdm=+wvZkbGju9M9m!<8Xbx; z5hvVX-*$ABJx;SGw4tz$wmWtenP3~9Quxwd{@Zc-}kUDbaB+vqJ1Yl0tV;h*0azZWBydLV=KBXPQGoJg0b`ecs2sDGJc>lOGbp1 zj1)VUrX&lsKzUJrT|JCEoYB6egh?;fAh|j6 z-u023Sq0oq1?`$WTnXfksZpOeI+_+)`)FKg z^gyYIkdd&-20Rjk(+XHwT#2B7tU`9Ju-91{KMA77vO7$RcoEdM0oy$T~UyOqRZpdQAzdsA@%KUXjHAi~sL>7K=|Rgycb<)-%)2%1s+rf2>Tb{}O=$B^ zMj(vs<)#YZP@7HyjJ$|u-CYHS9II!F-%)x$J1v*NBX2h;Zm6%0Kz^AI^yyKHog@rR zRa+<1TLA?*y}uJj#O%_n8v;eqre09;&uU*s)BDAW`THFLl`1_p^VyQ2CPCm8;-R}f zlJa+czRmOplmv&gOTKuv48f@cMi@S#c)BKBEJAjjx3aPK-1n4pjY(AC(Rj(|+2u)g z_=XTJ5?5I+!%e0WDu8#z+3k1m8a6Yx)$1j)zsl%z$m;#wZ;+x*@jK<>p=;QE)M7YE27k1qs8N(qVoyar zw4Q+ePh@;yiNF#~0N#3ha(=U1{MQt`NzgLglg-DH$y8Sj3DNWp1v?Sr-_`CIyhg1P z9Ts^0VsI^*E6zZd@2JIJ`>`0u@F;;#U#@JdScBR3wjD_U{}(|=K7M1AF$>>y$34V-Btm#=lV|?iBY5?Jpi>~RCoB(aR49-DQnw1d8!)Bhc5NznWI7v5cl8Io zxn+>m*9R8{JGj5+)VX0frRQPwQps5Uu3jYpwtm5${?jGM~J+U~dD!1_Ui$`4>b*-afKDA|7D`H1eH0gutOg z5Pq1Iziik*P@SZCTB*#P&efgutNYbTw3Yt>#s94YAEJ1Tdp}$h@MxP|13&suqjAou zT*^y`KvQGw-b3dG_zQTt41H5K?$O}HqN^Wq2_@f6;J2p=(f*CHoga)gYEX@`a|GeH zPJwzOM!@bS{62@2w&lS_`2OIcjB5t}QU!ALIZDDRO2Ep7eGxRbxz3_Ns&jsFit!_Y4oqRriKsqB{`a z7V!KRcN3gNRuL_^tFW<07DQ9 z&ym?(VW2CWR>{DcT$h0!4vk3T_E~eNSBB2_dFPPx7ye8laqDepn85RAMkPE_-<2{p z-k*{jM+y@Nk18D6iYbgsOIkPXr~QL*em_!!cl`) znUNT=qS(zYYai3Epl>Y4d{O>61WTI>Ja~43cApTRcX#?ijfEy%}b0+p=M(>Oq=aV4-ofI3YHeA8Q zT#GZetorw!`&nd%Mh9eCk9)cYNYl5~y);N|>B!JxtKQ>{%R!k!XUyA_zU?0_Pa@3L zXD*O8{h`i`h{eBhuN42`k|_=OyQc(b$fVtZAf%0W{<*UxzI)7hWvV$RSApa7d4R%F zB}jYfhg;+Ro@v0*pUTzZFziP(zcV@!wSKVfRdLa*ltr{aWJ#QuY3iQX$(exCTF;1h z^HOkjEUJ6zqG}YZH5MMgT$w%^Rnnu3^CX0r>NcC2bZ%-^)R>=_i-4vq*!0yX>nm2; z*|0AQ>u%Wju!S`FpzyZOf?s2J>}f73f<+QFY$Jr%fFNMx+0eosWNCO&ArJe@b5YK| zZGw-+18?ox`!n7)wt2>q6p?zJ@%$~d2$5j|YTaMA5<%U$+QCpaFHJsQb@5&x;o4Hr z+4dZ&!=QD6vUL#?_}h3js!|fQ=O?0Nan&n3wtsrhg4{H&f1{|t|68WPa2^0t0P{-5 z@?0^ep;WZYDfxNb;X8Pt6AHo{dl=h?t}<)*D*S8WSEuh7zK?KJCbQwu=G zyp2xYp`T84w3HfKJ*iMbc6m^FOaIml5b{dXGpK41Y_a@!5X)1;@=+@yYW$eME$206 z73b6<*ZcHXAoyAxB_JQ|XmCJg{o4)fY;pFnA?;ITW9w*hW2b-*Y~OiNizJq3p8wG} zN6CZ+`DtgC?_K->rw)gdzvMzr1ZPXM5;Y=9g!F6Ue+AU+npw21eJ75riz%W$`UP@0 z$r1s-Kys^!$6LfHUyW@t#jfuTE{lamYIF;&wL81?SIVd%3tt&}7AzgeGe(hKNvp=$| z8iWQR9QFQQmX4_I=RBBwD~FiQ4QoeVA7Ll{SZy77D&xb`N~6}ch{{pXo?T9icwUDo zcI@#*faajVr}8H4(dyiDw{>owvLCW|^JMq7X|X`6P)YAQw7GTf?vz=EkOJTn0}i4q zvZwvt)79vn`Ri;aKV%urF&|$=?Lr_!rgN^0yKMxE(;MUUBr~`V1uAkYi;(lPi-_E0 zXydgT@iV$~fwCZNlSAJ_L`O>&jm+5IiZ5;{p-xFO0VVb`+^o*5qqaGKDZ@cJTsC&k z?Hbn}WVIj@JX%O<#a?_~Q(?qZviMkK`TPq|B=TyrV+YoTw8eTpPR{wsrFa5HC%aYk zAe;yA>USA4qzu-PTUrcS1|%7o>j@kdySMqqp_$``CJd8;k{>MvC*78@jg_%#VZ_+w zd7ZRU@)f%int&pDjw#G*FS2yXH8P#eYSz`BX{+#vkqIgChASxBIPUJ32NQlXTTSMR z9Klh^Tkuyx^((9o5ty?e&eg5O|j>Qr2tQW2!p)Rm|o5}M+DL^ff z>Rqdnbi!iF9aV$iPFs6Qn;UVT{A9)C=d(SP#UCkwwyI~eH1-ai%Ljj2=h(FZmD{qc zcP1|Y|DzVzYf{{GztLG339i_HNPY@y9`tYdqo;qU-3pJZ!8U`d{v4uEPj0Fj~dim*Pg61BZh#6g~Jeyt3+E z15@F43G=#*y5V>XKts?X4-Lp}#-r$N${Ibp9iSII`p=Y=ISt!%1q1yH=rv6;+FA5X zbSs-p7(B~oI_IJ5#ZTSsl_WMG>o~T7w16pj+C1iaeQR4WsS3(PJ7K#R?`GFaDS_TI zg~h4D@=o(zX`97<``Cj?s0*~lEy#)Wm~)baX+=0lPPWe}WCLPb=j$-FC2T7`T(wZI zxL|IJqd4XB={SQCZ`1Og|ZlH`h!gy?atJE?c(AN(qn5UhjK`<15^ z>xFF8mO;dTJDc{#$pSs=lta4b66VcEGe$(+Q}0XOxpp<6HuCMpd=JRA(yg>Y3Cw6V zch|_RPeRT11UAigckR&Bb93)xC)Ckf0l-lbBG{OWF1xe(oY_lmf(WXkCo)Z^g1an> z_Kb@c$Y)>?#wn=v``degJs!rofsOtyljsZjAX(;KgON1R}?M!cJBUgQ?+ zFv^zTLXRXLA3(Mp%yX{>U|UT z#*ku(-MD)9#Y9ag{p3vhb{4U(^uo z^s#FIw1DuI4$Sl0QoUQ(xXX_TdxGwu@U9bE_LE6!?WA(~qLGfTu9F8u^6iq5RT6}~ zSr5;ea-V(EQ#m_*mkcD(!;$Cw01KFW1uEOj4Hs8RHdXaS8^4J6mG3-pY6KP>yZ$)d zXco|cwKvNpYGk)AhrHx-@DFAaKi{-mUB6ZK;~{SH732hII&jqap2-==1bRz^7GjVM zh?t$2u;P#XQ$wEOc)y1mAktjDup{nyQXpIG43~_s=}v6aPKcx~?*6v%Z2$iE%mZT~ zEep*RdARs~GSZ!Ur@gcRd3J{%<^QnCJk@kFaIWrs{-#b@##W)(0s}|@D9=y3^#tcfD_;g=*C77_ zROc=cqe2&y)hi&pOQT^=^1O1{{q@+C=BM%UGsCbWK0r*E#|)}L^m}2&MnJabwbPP~ z-o}ajK&}WVTTZ#6MtDgUWQBa~`iy}%;rquazOo-~scW!W57o2emA^N3zh{aDLTdX1 zrX&a?IecCZZb`oYw|(vj@l=Zf?xC2SE`A$HKSC|RX3F5+i4BErz&ee9vXg}a5dchy zl&kwSt&XPtcdH^kk?#tk_xir%Q!_{p@D4o3q)aM7A)?}*Np`B@;ZEjX;p9sBQ`L=I zpQljWqm}R%k!hvW0MNF>D~1#>7c3Oq?HRXEw&71qd`>?Ol zgrVJQV2V@^ZVN8*Z#H#nTJ{gM5-JApu;iohqg#dgDjiLZIUeuMWK1(mZBYU?)ZkV8M@()~k#X7M zJWr&6(7t2(Zezrq(H`Q?$GuFl|-+Ti@U@l{Y#^LtDn0+vFnGOB|^>S7h@qe z?3rUH5!%X8AczXZ-WQ|tuN^{!TGs3iFOEfTrDyN%ztL@&r%}o{L5YAINR%~XZsNbf zn}+TdP$2^tk14Mgu2EBmE`xP@RJP4YUHCKq#|a%L2^ATpE!SoRmb}FuoP0_b6CGQ_ z?H|EB$+Kv~A{Y^RW7T8mj}0%F@RcHe~9y++yX6|qnQVYkj8{`#+zsy3Eu z7o(&aH>=eW%`JAG$2*>k#Q^}q5oq~nwP1>alywviFV!2bNgjNSxHW&8(SGMNuI`EB zs$p%Dq7rEC&oWxchkCMu=DNgm0CurmKBpTs0qgKl@m0=Dr2S%>VJapkYb0DH-FfzA z<*($c&RXG(T2qx%5l|FmIEhc8qtH`k{90``xsfl;@wIfSv+wjthBj|`{_GxqI=4s|x8yhBaO7s}mKLw@d`UaKKjGoQLj%r~n@joX_6 z;7BU5hkk6(74VE!nl^yWNpF%p{K+*I-VOOK=ft2pVP2jQN@p+N&x{WmdDk&Ef9Z(( zQhP!mrY9IEcAgR;ckW#|xS(#ecrB(YxTiOtY3gmNg84NYz+}E{l-B#ZoKt5?JopSR zhecP?wxsPgiiU~i@3H`N4X+wfITj*tcIV0Wsx=lzejg?uz!D+ePip57n9kFll&}wn zNY_uHezVIj@=V?Cn1=Sb-1BkjovIZlbW3_N-*S4E-|ydp^f0*XF&(OohaDV0`5EH z80_b)%^8aDj`sg3$J|3!5?+P4Fjx}*-1!@>5=%VrnN#NkNEF<6GFs0E`yR@9Z`5)R zfi9a@9r)iGyjM{r$EbaUe?OzbswW`@5{`Qcr6H)34aj>L1X1X2Pu6m`$N0_2Y|+xQ z!0eNNDT>Fp@{tFZtwVmUTwi>cjIXW^4Ici%R04!f(U)%Tkn=j=5~}cMJfglXbULH= zcbH||U1DIqf1Y8QgFy4jmMxm-I1DLQi zYDmy*zsfj}hN$rK`5IqG13x=iWYhIdzDYP1qMRaVi3> zfZmk4UjTguA@gGOo7S=h54@7%qDnx$FxuQv+Bh0Ht=z2LCjw}iW;JAOvE4yFu#(<| z07kBJPKXnl(2gv9%R2-s;iowW2b!(C*V;Uq-I^wcQrmi4Dyl~h!#kMa0M3`A6!_{m zK$-z8HjAKdE!aW_QNe-U3#JZss1%6m|LCLWNQphz9f0T7iFoH~ggC3cg5wg3(Ca0;t|UC;n6 zl4)9lXQWH1xE6*3d-tZI71)ZJ=<~@LCMLa&?<`T)wRQ1n$V&oEHX$DhpD?&T8%-y` zmjPB{UvUYJ%u(H^#}x^*aQ?o7d*;%mM`4mQn1nGGA|N11`Zo*k;|Cw^ZkhQ4Vt0Y6 zQN%yWREgsaO67>wFR5BaDt-5SiqI&T@WyLXs1}e)V^6=p0e^dIbdrGGy;=zG1W;$1 zi*I@v944hf;pCVHj|aX}n5Z*o`A6@)b6>P@ec#F&%!aX8Vl4XsLiB7gh!K)<&g1993xBX3`~I8D@HF!`$YQp%mieY@Mr5 z#5Rn3bpv$Xjz}NhR!@TL{ZBg=quxJaJqSTSva@}8U}*?j$@hogc>^}C8Y}Cilb-Pb zeE(rXj3EUVm8($k5Lsk0W`0}fa3TJ4dnD6FE!SERr3lf8cDAdp^~umnQS3b|Y}nM3 zEAEBGz5l&GcGRHOdf>jODG*qyeYc88l)6JrBR z^+WAvgrwJxS^m7s*YNtbgwKsxJ7rUW;fo%g?<3+?qQ`+Z zH+EaK?sNv!x#AdGL8tz)=5I6+(DMM}n6U6HjjwPZZG?Z>KersW2gXXa!_8UQ2g12k z*n@lgzTLucNv`cf_~xYqa<(5DJ*Ecqqb3aKC$UE+nxcLfrcaWoSE1-t-Q66XF?&YE zr8(8iXiNYg!g#1?rVPSIJFRpv=kUf~Z}Q(#ZDD_bJ?1#C7iHh-P!|z6c`|R8IhxB# zeJ^S*%`o}@jEjAY&xm6O-zjGI{;q_Q@d3jxX_jWt^tUmu`ifz(v(s+wI%qu#E}Nfk zqlunWfo@3gF0Zq9$O?}nxq(R;EuaC8<>22`A^RY9id>O^0pl_|K=YgsFp0ro!9XJs zLhs*l`Qr5doq-Koxh~xzjoiJh9-p#4BvhwZzO_ue9{M=Fe-pwLw;n&T@J=6Sw3)4k zWYU-wA_KDN)B`ldh^RW<8?V^|{(iH-n$>`Gfzcd}BkmPmK-!B?e}HtSL(TFOQvee( n>O={2>JktR8~cIO-3mxNFu=E!h(Q+AV^DzseJx+eV)zGke}au%k+;RAhe<0 zYFe%;MjphD&JN~Qwr0exUXEtOW}a5&ARwNr`#IXqy4*?PAK#+Cf|*05h0N4=Opjl> zppd9bkF_?QJ)fyXmyBRyd7AQM&{-)tmoA=>7#gEu+`F^wM zNfa`5x%=X9aXHmrzv;mm5PKimw-lw^5pkGmYiF8mec(lnG zdESN}z1?Yd^lUah9&bn?EIZaYYXZ$Q9VTKQ;Tfb}+ef z-}Y09r3%}7%9wosOL~_`hcqA;2v4~c5;(QlHIWmvkAirrc6)v@IE=1iVE@<;@sApt zs`rmse1rV?Mp%pAgCyNU@BH@k#MpBw@^lV$eE9M+!0&Lq+hE9`M^K|CpZ3}A?(SjZ z_r(v33*)^pm8DDhxAvh2jD+mYu#FCGhFb4khi!_px8qkU(>qd(Rs;V(7GZ>>+Z`kk z1QPArol~@Q49q`wi>}3EV;qmO>9xCg%cR}3AnpmY1}8HtgvUlygc^$NbfzUS{` zMHY4` z^p%EaIH3k9$*k*|{-RMgo-5kOD%bKvilg{r_wxail~h@!v>z7*7o7G`{drE9AE%Ux+O(oldT4bOjYa!64O=H z2Z@WR_BZwUR!hq#MeZutmI?u7KR)xp;g8;({QTb^_aQwmPl5p?7u`UfNr}oD>wF&t z@FDB%6sM(*YH_e+P$jq@h(}0C>ovF9UG)I!8iKg3xLo6N-I?a${<}mMUgih|=uHUNR+B{u}{6hP!8+1jTBUF5OxE_-1W)Z|$yiRy$xaFYxXpHCqyTAKhi%0dTfS<1XS^_-zjZNYbmBFTGf z&nKk6Lj1${H`0)!xiQ5Pr@;xz>Fpp~dry0ke@JaodE18QeqBNfLgKSHIhY6kOI7D; z<#E2|zN_|T^_P^~9)Vo1i2c>ch3e+4)h}oWm|%ibP=D%Fa>gX|z~)&)xKY1p8TUcD zWz!hVQl72V=$zWH~PFm>SY^3-&)sA+00beti#+L_+79s zZ*yyylHOy=_b|-}A>B`9U#s%NZm9|8?rc;wb5BG0`givZd;XUtZ&m6V)3t~PIJAKR zIVjB09j$QLJzSt<-*x1Wa~>B~LBwoFfg^!MoqLQjaaTBNLfr^;KIMe&R?N4NyO2MJ z8Y3yQV7-YPN($n+FCO1U-)``FJ&SLv<2rd$l@V#Mkx^rqe)`TlQ_;%yqQ2u4KnKI^ z3CR_AtR%(-m9!rXV4%{7eQWXtk27UY>p2U%FFrsz=9*~B4y>g0^L8YzfsjGdr{#?E zvCsRicMbNm9?$X-D&30&-TJVe8K%RnEJl8Ule)f&k_Pyy)Af5nWP&n{0{M1(jza)F z@4AkZaZ2T9XQgPB&3`)ma>9T+ax`*LHA)F+DDN~--U_?h+`lS z;80df^^jLZ#AL=DDnPCkE9R0)jWlL|JtzOC)YyIWOOmpb6{x>_+r3JKhJV8Wz14XN z9Br)|{7CXoh9un$cX2)jbq^^~u(^25-+q1p-I9nUr7WbZiDQh2 zo|uOAV%LG%r?Z1^N|A%f{Lt~skB&WxI^UZYuA068A@PGKbC(Fm>wxMC7Lr1i<1Ct- z@E{7kHEU7hcy1$hf_zn#_TQZW49t1Z*iV2Xp`K}L34qVm7C=1;Z7YnA_KQ$1=)LJK zO_B(O2J9m&n*{?a>QzNOP#i&>yhlvD>)?C~ondI&xM;A`cHLQv=E&GH0A9#KtMeg$ z$PzS8<<7ZwzP55@&Ym0UK{-fvUR?;37>62QL3%VeH>gt(B!z-_8wp*Oh@D)$W-45E z1=iIyZ$;biFw4te{Q+#lVfze;cEO=h^}}wf3*(-Ys^%x|eD%Rou0EPzb*F}wGAPbh zBQ!c*w_u*NEr$D!J`X)pS4Qilw9K$HM*EYSY+))ZcoV&uZEXBlVi6mtzh$ZcDb*^O z8p?7lx*t9mNm5>ZoQT3YC}UOa8RicQ)F)t)j|3Yj^ENFCybs-RYDb%FZnDHEWfC#xM zCVTS1F!5FGI6@$v2TC)TEjYP+7qrn)K7OP0|+CPC(<{13k8ty9$ms<#5TwZ8E*u+eK+eHrRO(N*)5v(Lck!4K7<6>&GfyP zvAy+fYTbMQu{w$8PG0I4uAU1ixSn!Ja1Is-wutH7CHoWLc;IPnF|6eUY{~ECA zx^cp>wU?9y!@^N49esL0#v8VdNBg@0uR35 z$%=K_DU=>P1CxWRMV(r>(cRD zolB!TQAPDFfmGDHgVC+{x~_iwiYw%V+Up5*Um)$H5kiB#_>8v*XDU3grAq+U!t$rT zHfHjVQQBx+Msq-7w94{^m9SD5paNlAa7Z@YfOd}Xv9TszD7mpt5INP#&BSWpJ}Tn+ zA+qXLp^vqlmZDoAjEYX*x}h~esEWxv0a`sc*huBQ;G-yU8hH1rf+DQaS4A#f8tA5~ zvXNF=2r==CJZjL_!`BfC&3NLiyWVZ#lS#ngX-R3H6hc~P^+V`}LTie*1NF5d3fk2E z8>D$o29Hj)1)Mh=0vJ@e&S)2Tnqnd*Z4^_Xd}9%E{5ZNv9>7xqBPAVqozy!cqG3cpMYhwh7{&fTyCVo{i0ObX+ARtzs~fv` z!+sZ&v2T@y6I0Y(9^V6rHBo5U8ELw-jspJ}+f8B1%1eoW*kUsx6N~`PN4WFBOvMHT zg&n3u#Nc_>a!(@)C&7|O4haoI%^sc5+@&2saMP-sRpxQ(O*!Wmjc?t8xMBu+T`@A{7n0J-&pwkT)LNLJ>sEHEvcZOBL48mNeDLQ=_ z!sI7~bS-HIu%P<#O^K3SYYvh@MiC3bt|C{$?#YTp3DvvWRWG}jFRWYRJz~pFEhl2~ zRtk1RLJh;)SW{jf5hhGR0(YoSsX-qK z96Hc!Y_}rH=t`i`^h)MY50!^8a8l|*zEm#Mxh@jdmH8>8jxEAle zA55!T#aMZzrS&wj1E5(TIEmB|vyyF8S>I~5Y!w|(`Jy?o&?uI@C$=~mS?p=fqK{HT zd*bzn-X&wOL<2HK!jP`0aMY_YX&Dh3Jg1`l!z)p0|J=iD*0F-d@7v5CfvoeCQ#*aI zXi8+VrLs95E<;nq#n`XW1R9q$5u4nO zl1Oa5+>TJ1)W3$NM2P8V!hzn9G^S_Ua=?D2mM*~N-ZAo0@J;THas5C()=vWU+6}8XiMUPeyfZ+ z@kFF~py{{>LWRdNG#qVFnaC<&Y(5XN8$&Q=ax`e+c+ITc%n_>eltea;Cq*A4s7a#F z#Mu}syg6T2{@5B!KV;r>=CuffgsAO}CT=`q9cA8fUgyE1I(Ht_9VvS&&MYo`wc2rd zBMxpr$dSGP!mvT2z3%F$7Z(P*RvY4D>!gX_*I^`NJt(z7Sq@8I61lvJK|h*&N~u^V z5wcR(>r^#uNdaHZtN@cQW8Q%G4vEmKDu|czKv$JVNP${=!2o)SI!q^d@Df4Zmt$78 zBseGKCB+>od;Y94W;ro=519pYOPECUk3Z=9Dz4CuwnM%>%y4-{`{}_SQxLut0!W4Z+4LyUE5h?;wDFbYkk?1^j;wVxG3TbcpodBs&6;-A*9^i|PG=>)1*>mL& zpN%-7Ylg^}B~b)qhLwsUvV|Lb3@?4Z(^W(I$Xg(jm#kJ+KRsMUR|-+v6us(NBnz>v zhK~>PV@-)XOO*UZh!{^vf!a;Frh?RJ+kW-7>(Y^tG0MY!HMW)>lBc)^q=W=iQLYE- zkvjXXv1YxX8#tJ*;~Mx30|#6+czjh|!;~;V)HJ+?TJ?U#W0<|Bvoh}ii9yhY5+37ur?pfFKflBg%B?JM*F>wS8hRyKtwzy=+Jq&=o5 zbR!6atH;rA5R%N+ez8CGNz7Q#0r8|3ueSyNM#-nOGWc(euhNXn? zrZ~RY%_h((qbC6OY8dJ3N|3KSB~j7+RrsAV-yaLwQY+1U zw7xd`9}doKaVDpK1<2010|D)D);m9fl+~n@han>!L+V$&Kr_Bd2Wr*Vh>^{0%4=fK z?s#oGhX8~Pc-!u4$fc_iVL}Q~W!`Tet{iHW73uYT@M_yfz94V$)>za(z8xb9pP$I5 z)biUjx;_waLPQ-)ut`R_)O=kduf$En$Qec5w~-|%USXC#@1Rx#&1{Iz^NvJdA2Rz^ zuwNZS%CDSwx{fhmwb=k0i-Xra*q1X_T03sb$5P;=ivvklK?S7% zUBl}vMP8l=6RJ`5*ewpBkJq#4%aZ4zjYwl>J|QDrbZt&&B|qBSUNkbJx4VzLX#-pi z)hwvuDFvR^o_HcwLe>Iw)jqVW36K)CZ1;*bDu{q2tGm)Su}m+w2zwOAaT5O?cSp<| z$-t=i)|}^+5-;m3(#aL_ooMDU<+i|8H>q{HSl`;^y#4C_V$v6+R55!hW#{0J9~{)t zuV95IxG*DH=Q3urK3J9C$e_tON|ltWtlDq1<1wZ?aa8&<3YN6k^~aJiq22C+kh{o_ zhLfMl2q1JnQ1Fc$HPCiC{Sq5Zo_~q6r8T9J9>Y+M4NJ4fpiBJh_$vwYY{~Cl_(8{Z3j(FIE4S2N8Y=Ulcul3ZYi)+F?#^5Y249frcA~{esy*UwZ{tnXHDj2rM*6HR2Yk zeqqs(6!Kgoe^m=MaD(Wak6h)k-$u9h4O)>U5>E$ii})+{V#zl8A2kB4PAPO+4_F@6 z0SLuuQVo(K(eW!LfkIJ*K6(AS4K^QgzHv_R6AfcxcWKawItD_fyzU((xRC%D?*QlD zdxP^y3P;i~LtPSVTDY6e<2Dy#4DQ#t=5Gh{{`nlcrV?b)O1yU!9g)=Eq9A`Aup`B5 zEcla+p>j=|H{736>8}_-qh4vdka{ZV>k-P~(H8ki!9;`WXNwafe3z&siy+laUm)jI zvwR2d(9rxTsdUxAvDK%NJ{Z~#8rKi2TCSUMA-@(Tx)ueUQmF?Aue-qKLe$ZsUSMF? zfsJ`gJcs3l2bRX7yk>n%7SE0caa?an^oon5#I_$wxdDb&jsV)bid?Qqo!=ZlQ*2PM zFy*QRwl9bS0v&e}FfcqN`y;@(*u7MmdGHQuq(QK#{90Lhl?c48MLGC`@La7Wvx;0Ez9DnG@=9C;6Lb62eHlZ(9hv|^Qy@jC0{&(rC1cU3>SGss9m?2Qy=-S7PHP@^T9UVsVu z(TA7VJ%lPabWOEQKk^kAF~`Orc?3}ckD|Dt1nCgys1h(k&u`cA@a6iyBWz(^??%kg zH>zYV*xPXcH9^u*5eaPf#$UN&2HSCEYqR7K8HzKN1v{^``EXzWm=dYL(X~6Fz zj@g#Oz!?+3NdrZJkgk;iMM#JObg=lXAcs5>`{Bp3;Q3Phn<>e&Dm~YzNIgB~sL0+S zfFthA&<#qevmIBie-!tDF1IEQH}n_9eaeFTd`ab*gGtcoiH^MLYYxTIOeHmK1@zL2 zqU6w2U~nIsZ~Q=~TeiTRMLE`Xnp4+jxeY$G0!9TB-nn6L@&7;iMhI5s*9m zz42{wWm`Q!vYE`BOr59Qid8QRQEq+>OsDE}1N}W=!Clpu!*5OXu_kRb12g7r=@NAz z?|9}P5d_rW(yURui{|gm?AM)(Jz999XFdW>!317a=e?YXLx38+~^j z>Aa@FsTnNnj==oF?Za-_F14qH)O4fPH!R{vC!a+~mXktfv#kLUv+T~R?6>;Rl>xDs zK(;baYh;}tJ?&RZt78*ZOfjo@Sxj)d!5AkZ)}+`fjDY2GJQEeSh2hK=G=kFDN zmdcc9Njf;0i;VPU35MqbXBX5eO0b6&ig{NtV_uF1lL7-XTh&##I=(vu4^Hi56H>h*798-iA1n@Av*a(@%e6+*oo z{$jI&{33-ohKN8U?#1`JLS;)jx1gn#`clo+**x?+D_lhS8n>C-9xb(Vw9rM#IJLXU zJ$E*TvqIe#8AAwp5k*pfl$@M`hL1d;Z_Q0O|kQt?tt3#7Qc^6Fv{i(;l zohGQRtb=XoHV>t96paHUFGMm#Bp&%jypNVJX)XY4A$=2kR-F^jFkcBe?(y>Qb z;Dc(eT_qvO#KO^O8Bkk6)X}Vp!_#?(jg&$XnH0@8mX zxWhv%FjEIQ10{`?dz$g-NH$MJ0k{n2JwpZhb5r44X&J`I0jOwuel04 zYp2dodANr+VwrTGk9O5ILOImncAc2#ti%h|%L-*LbSTsF?OIO8#Gb;eVwJBAYYu4Q zcaC_g=nJ4U+v~lO^?gNk_BwoYCh0J9j*pI(U)&tjoh9ktG}s&c+0G_xPSqm7J;4bu zYeM~jwgYSzMY)RAOyHLMj*@znk}SGW9|gOPv+r~lAkb{)p<)@DWRuYP6XwD?*hMnL zY#HA!W~3&av8m23S9rLb5Y^DX$&$~4ow3t{>sxIIl>uNj5J9I11{eAGZ_(xhBGG;z z>a)f1*w81m{+TE6R0tZGu;I>8d;w^;hR>52NCs zl13UCIO4FPaT4V&a!Wll4zYM; zA@lMXQHnNj!24C_ABr|@%5ZS7Z|3I}3?|Co73b`~&%yApOjMu89*IC)XlTp^N-9^I zORwE=u$|7pfZ2mq*WoDIf~RP@BHtJ;`OI}b35TPfOIk6F*&K-fOpUMMKCSm4C9m?M zX{Hn2brS(|w(4B#r{)DFF2$@Oiaw~rEDOUhvL`iCi?5~WL78aD6v|WVKi6ul_n|6- zUakkYIP9sHi+r!+JklOpwB5JVjtFQ(EIz10BrS4~?ohen$)N{*#5opIVE|pLCxhwn zW3Ya<2~{s(!4qEtA`xdEt^G94GFhr?mCZWGqFlTpFTZF%WWPd?TB|})7by^}$^yYT z$+lA~eN|VT`-)lC=Nhj&B1tCpK9}YB18$14P~4Rq1kNFK=x?So(bZCA5-R5*pj@{oiL{*dZwjZf>eiU_KvXF^?i*A>LM4o z{a>tXGE@W`nRRJ0Ym+PGBkoPZv^)vs_5>1)204T|2wFZ7S)HO=DxdZ}8{~ z^pH7QDkCw}J-%4UXj8S^qe4n|%GvecPmB}EllNutoUXTpzw^bbCCTpE{_LV>%yh)h zxrh-o1>uBwgjku7J-^cD`QFKn4Wi()A@=rFK7>O4aBbMk=0|xKnE*Sy-%KFfFVZlA z!gmxpY4RrI`}hZLh5)!Fo4&KxiD*ui~KlH^LUp zQ#Ts3r|cc=LnJh3#Lt<{SF$@eU^~mV~U>2fMus!ZTQ=1@13UHpQ!*Dcw zci(O2`(nK59JJ~}l*tu^wQC#wW(Nljl81$+4H3y+CD#k7rdlTOcG>1jZGHHCLW{7P4Nk9^st?YlR)sY0Y=Q8o4WJX^Y(& z^LMl_*i%_EC-hrA<7Lxo_t9lB+Uj1Q#8|&qM2v-IhJ(suxwPBY^bEfmcn#&YM6fHxp|T4eE0|)_7&^9Td0kpr#$L3RYXmZd==i zF99jpB+YungJD?!@>J7PFFMd@s2eKOD5O4S6+N+MG={{t318 zE{8bpE!-5sRhC4yQ&^U{goYL0Z7zvR))f8}i2+}y5mEzJWSmYqqKx(Pa)Ghw1IOZF z?nPly;W-ozFz{iVMkp>khi~<6!S&0ta0m|!zojlXByKOOV=>c^ezw>gGskPen&8%p?2 zpv2zsxe3z+&b2y0lI$vhg?w_U%Lm!&kVGd&aHTq@lN>CSJw@X4#*t8PpL_5_dNbO~ zB5ocRE~{q{h&b`JdsItAe9_^pH4xQQX?Dg6N=4O3PZ+#c|5;=N!Rw_gO1xLPX zqj~pc*R*S}eop~ZAv$MST~m%>>9;anO&VM&5AnDTpH1A#07PmKtl`|?U*Z4d3O;fe}>Ufrj9CSs&EI)b$-#{aeM(GQI;yNKi zrvtz3E{cyIzGdTSd?tq4EDOdY{9T`CvwXd~jvTcr4%jUko@5Pge{RU>0)0H+Hnwv@ zD9CP}9!yXJeNutY6qZNS!bd-5iR0NSeIuD?9hSAI&|8#{u|aEcY@VRVMW81}h>jm> z1fqXImAT1RBlZFX?U~^5MV6`WR#h+O&8?7bKOt!JXBCg-d5I$2LygaJLznYbC=~hb zqF@?jU*!)YL1nU`Ir;Gt(l02Wsi?6$OI$U{VhqjMf0*;Rp;!J*mmsT0^+$HQe6s~} zNq8V+vllN`5Jk9@i}ttp;&WZ5gEs}rnk~Kc{O{`)gK{~XTa_E^jqKGc68!3+C<`=t z#pkH{O^IUDGD-jhKhSk*QCA6o;;9?t)5$|s<^gq7g|LgFpC*D&f#V=|AeQE0OLpDn z*@8_XAvl9ldnC@`LYWeg1goVn5&Yo z9YDqdmsGUmfHQ-M74X6qL?q;%JEmCLLeZZp;n@zO46dD|5cCs zS%sV9m&h+QAcP&hStcX?75|KQ2Uy6Xjf~+o{{_8Jqypos8`Jk&q%1aa8ix2_o4&sM zp{kIWraHvBVB$x>&e2}U5h;a#I{Mc^&!0|6mwuPyzdZt{E0`uuZ~<(;Ip#(~GbU=I z3pK$}3UP*;f6TU3OeguKgPMtm<3r#OvriXammnO@@gF$YOt{HNbh7+9j7pw@@sHAI4yo* zU+ka?FygAOr*p|&L{8EgL?w?@QWC|%SdN^lOExBvKZGIrC^$_YDFs%;OT@KkT=#=Rz9?=Pz3yH-qIu;GthM2-ri)A>_%D$E0{2_D}Ra26u`4X&<1E(|eQerDpeo{?#c_w*BQ8P;`DQ{;pRc{3~6K@+6Zc|c00a!jy zo=*TfGgl*GPdi(C7amW3(!X(eKCl03W+Wy4Tg26dpHxd;iCEOZ*^HQtfsKKQUfk2l zorP2YmYC1k)SO2}OyZvqpLhJEmaeXjJdBJU9v%!HtPBp$7L3f?+}w;zEQ~BH^q&&+ zE?)MoMxONcE@Xc}`~ySG%*Djn%F)%z!JhapOe13lH&=dA($9Y4|MJhyQC|MP;q6`i z$-*ZejGji0jLZy7jCOX6|El5QD(?OX@=u5UuNp3DpJnEZDrPPYZq6oV;_hblu4MlT zVQTW<`i^eSwtt6XYQkt{Yi9Q;>hfuo`QJ=RO3N$#x5i%-SXkLP{;l;%_P_IXSlOsq_K{=PKh z;^O2s=3t{Yr@%<0WIOg|yG%$PnQjM(V8%-GC0Sj|jXP1sES1wzr;>N6{iZ2#4(zo1M% zq0EiVS=ddP+38JKnAzyrOxc;~xtUqG=#AL9*jSj1*-TB?{^5vz^iBa9Y_J zS(q_8+FSf><1fN_gq5WENm&?}{-;IB*2vZTQ-PmU-pGVlQT2a%)U52xR9%h!qLZ0} zjgy&^lZ~C7m7SZ7h53JkG|Ze`K2z~8P-Z3u)_(y1H7-1#(R?y%^jD&O0{pG<84Qo8 zvzd{rgR`20gDpSlUp^84rTKS$6Z8FJRHUq2J|(>VO8I{#y{ehhKkoi92W+kW?jk1s zJ8yZ6O#WfS#mL>v^lw9-djIG$u{5%`F#DX~|IDcWYPb5oj24p-GdJ_+NpEay#!b)0 z!Np9^#l*%$&uYYG&cepZ%x%WW{qN{54(6^NM$Tr!7N3!RM)R3Le@8=1{SPi_{$1R| z((Esym_BDW6DvJ4s~QV44>KnZD?2R{7Y`E?DdRsQX8dbj|5wO-jQ<}}`2H67mxbU{ z?;mZS3(V(w#rWR~);~%6i^uIkHAMyLYbp0<~{}BWKk?{X!*Z

zA2IMB3IA_){r^T6?Ek#-nAv|m26=qG(0F+!*M7bXK^n_Sih+Fm^(p8oPyTFyag@?_ z0Re$S`|AV+$;!e0Y=m-^mKTRQ03aZskO`SF13*BqHl)Rb)jU_vbNym5)U$>Iev^c{ zO8umWT{D7?Ickuj4?zSQCk@#mjU=k@og|Ko#?CYoE5SBJhdE`6PT@JKi;fPY{!>^G z963TA0}qHCY0zS!i;=<>;UP%7b?3=dC~perEXSJmIq|=(>`-@`N_NFP(O9ajx|!FR z@|t=9#rzMBlog$6SWkbv0@`W$w{G|&cvo;9-K!t?6~7!qc?xj2pyoj=7N(@PbhbjD zxx-J{mhtSRiP-yS1K))lwqy#hwm|GiAMee#upO}jg>jLi2%k#+|b1%u=m2GJ=F-TrII%gSDe9 zCq{~sWER7NNdSsdbjPp+n4p$cY^C$*8a+TU$u=^4mCBnhbfS+YV7j69Wx#&dUWmT% zB{ZOAgq@zXl_Wd}X@az;Om?Ua|r}>3=(i;Y)aY zZz{xngs8iTqVV$sus~`lxIe6E8&cNYbeK8yJ$MIuXX|H~O_>{l*4@;n-I_xc;A@4p zlQtP4T+!kwneBBOfYE_o{4%;k=q(}t?mKSmyGt9`(mTPe^*~dKEsB~rV2GoXYBnX> zOW!^zE&JT>;Er)c;;4?8Ts7K0X*E7QJzNCXp&2*#tqR(f)_S3i*_OXRd zamp>Hqb0*jhe||FC}2ALx^>^K=s0fdZsG%$k9rnR4?0Y>-~x2qc>rvTlX_$!ryuah zKCjy!{WN7roe2)@JeZ zK2SO3uBJ%mLiCz|`tU`a{*o6wrXYNGxz*;mB$Hj9fWcWtlsX^Z=v|OvE@Rj;@B_JH z$`PwV4v0rj<_SEVr^t95PaH%iu&2@Zp4b>^F9dM~p;AeUb`Mw@=Ha{ZwaY}2g1O8? z@jW#%RlWcideFhj@B8x zn9(XAT1in7)=;lOM%J!eN>pVj&mjFV@7B8Lg5i2_mP?+RVID1XEi8UPR{zKXzTwkU z;d0IsKlL!~@ccAUc-0^Aj-52*3qE~i;4ev!eW{#f?q**`B#)$l8Fs zw1Q@`vl&8Od<1W>NxgeDK>4OK_SKw_Zs|J(We5whv@E{i04yeQ!Y3=|m{EG6h6xm}?M5ufg=sh1g!V zR?HPNR-!26Uv1mM$ytGZ6_jZav5JG_3|34#G`JeQqSN4frKXC0v5|rxFoL7aTniwt zqUbIu4N^VGk8uZcOOClT;+@waDczk#AWseW%g9g0Nrw-h{z3DFHDkMsip1Q5ye49= z1&({4vrc}Ltn1K2T|+=MaoT$8GG_Ke?R7XN5bEZ!+C_@)V^)j2K9alx=Mn2qb=QXv z1re@^Bf@!L{qzs!h+UqwB#GxcDYPaz?6^}~>i)P3*iO-l-vGr*C{uA;O>v5+1C$a- zKZb8~P=VjRIXZd2E17P~a8`D)$Zh&%ZpR!Wy-MF4bLd)lqS#-#Np&hRyx)H^OEmXkon2r&!Si9fQjQM|fN6(c zx<(cC1XJ8d@P26++H(|P?m1zh~IDW2O{ zz>b3Ik8*X2fA6axe}yZBCU)5)eqYeOlxPk&A3B*8*lD9>J=zG3Li^$A0v26Ggc`!{o;@(l;X51chP@*`+a4GAjc1l-bOUf5itANwLNwgz- z)WmZz2Ubt34B$1aNjcK|@t=`b%E^d*@x&hsV6D~?Q@12!2w3_EPz}}BKrSmtYn4r4 zyfsf#xDx%V%wHBG(|%x~>heDjC)wJ=1TbGai1aYI(p;JE5fU70R8)I;rL#DSQ-0>9 zXuc~-H>Z&PfbfuLbzz*`!wp~b4&P&{)?GT5nnk$paMy30^s;3l$_y!!i#t0mggh)H(8DZs_TFl z@_thh9Ybvt%sSUa|B_12oPO6NvHRn+_N8#!eO`-iO1#luF_p&jZ=t+F1x0qGcpL?K z3J{~_1cT`Mhzi=naw$OrBvi0^V&0|Te^sO8P4Te2oYN-a2WwPEHiALj8%;zJ{B&bL z%2cyKsaK|)C!w*RBBS(1a#Lta?jkEvh(8xuU9D06El|YwQQ&*f)VrTZ`R@6SWjg8S zNwAcuOXtNZ(&Xnh!Z`p#%qJysI~oaDhCfAA40hfPiHR=tn8tZwNN&1Y@A*#IvW1!Y zYb6*w-%JFG+?iG8=p+}B)#>UF7~~I@UYjPGy=-Q|t)_vIF@3ePYg%@$GAx&`Dqw;` zHxE^Y-PBAvqjho_b*5%&)9n3q{WtZ|dABM*zr`J}xA#P92)yR0B|t;= z5M4>~ebBROLeH&gLSd)6ABDgUaHW$;;iSs77h;zX6|Ho$o6b(aJle)MdxS|puBF7% zNZd8$0}QsGI746g84EpdHX;rUaJShVMw0^ylk^{WKB&d|)r%B4BF<(~Yv0~~2E}F6 z%cLPy0Tl?5A}o#Y0JlE8Tkp{Inm@}yYu$>|4!IxCQ8aZa&tR`1<-`n@{4Ffq@XZ=K zP(4#T3-CR`rVMb4K7928Tk zy#UM1jkI3OgPSrXB%H%g!Ti}2TToz+eI)JoDK=Ffy2^oEPfXn&D9;VYT!vtIL>d~a zct`x)9+IAMJo)%E?IpPOTc~5 zA9b6$G*e2J{8kE)8Tm%!zw?3jdla{G7eDQI-EqK+t{!u-vJ~0OU5{B!ZUP>H@S7srb zhz~>hwsM3<*n*)V0@-fKd-|Kdi?p$L>yhi@!}RkOJ~jKOl}v>)#e5{dKAzbPggS^W zzrQ;&GlJK4DK~Wy9QA1iqt6D&w-h)hnqhzQV5aY47`w;@GPwJi z&PoIyb(9C*rXGzOV8QAg-}$5HC11qpV?Z3cDf=H#eVdWZrYV6+h9^?Q+Hs0HiNoM< zb@S$t5cd9^9;!S|BV#oeZ+aIu5iT4)07QcF#T4Ip%rs8w7R^&M)|x%g8`xw=_G8jP zRgO=&Vk$kg37X4NECz?iqgfuv=h@WBUE`-(>hOVPFy*pByJ_^Pu^y> z>C1r|lb?LKylgCA?VB<@WqJBf>x!auj<#e4SB@OhKZ4>Ee$dHzi@mNvMHQtJT(fY= zm2n+T_L|WpoCI;$rOn6EJeVb}?c|`Z(&WeTNew=Ov*vuq66pJ*v~vp2lnC1L+9}E! ztJvb`dYY4^xwkDO`Quu&cCm#kSDr0X$$efTD10y!Ii`VrnxL|JM9OuV7>*R7D)gvVMb=*Fqk#xN>)Sz9$w}ie zE9+4yu&_Hk-|$vjO$S^Q-)rs%y(IuY*xx*4#Jk@@u-IO^=Qn z3|o{A`&u^hF-bJ{anHQ%$4{_sA6L1O3Cuf68^|&WSN!atOp^Z-?gtV09Cuv619rAl zNWb5RrT6?U0e^F(2-2oS!tEm3?%1aA9Ims^u%Q?j;H20@f{`!j_bIFLT~y)uT@8@X zyp_*Q+FXd4Bx}*-2;nq04(d3vu5?H*N#HtNKg<9L&FlGGq|L?GSf{jXT>{3h7P!@T za=xs>>ZHw1T<1h7S$fd-0lGfN8`=eZ9mJzXU2geEn~RC>Bkt5V$BQ=s<5i^q)A%L& z82aTT9EFhnW6k!VIcDIGiTJgtd#kL<@>bgl+@XSah-QVI{sbg!^Kx9`B!43my6a(oY4u9 zHt(zM*tuNqyGR3q(=iyYYT(+?2rpWGLBQK(5h*~S)ezp(Y#)^nX|p!Z2YnClQ($Wm z$7&1k6XUMQi!l#^zUo7cT=VE;vj9hwMyE2pZL|d&IrVn7v;iC4KNkg zxk(i>4F__+f||9ZAxxtcq?bhP19=fUdTBGR)zT*OwXZ<>9X(07Wj5-8XMi8!I#Dc` zEkoLKX2KhWWAqbg=|NwM<}OW7)2OA**wEf6u5%98nTuqN&Bbirt>egI(+_c-S-4L4 z6$d>3LG-Oe8%Re#uK!1CN_DRxb5!4@NJr@nc}SagLU>j8HYRO?$S)TENj!f5a0_q) z(pf!MgVKzIcYFeD3|X^hx>r52Oi&-kbxv8*%YdK2^LJ=0#I%b@A1rN-tM1hCmo~xo zV>hrHn5%rpC3Ldp2;h-2w6-b3at-N+ z;`!Y!DAE*N6XdX^O>PfB0}`jSFSa8u3yE){&lzq8?hegJBvjM4XmOq9oN>XwM@Q_n z318YI9}5X?F!!W#)nZBieT(**=F@xe{En*a;=llDb0G23CTZ~e>A=GQd+Y(`r}gqp zl&Gri5S22<{do9}m&%YmdK46a;B(A5)l3s7^j|Y%B+-QArzsXYu?|B~JJU zfENSyXhPz8PNn%am}<(fShKxD012a%HZ7C(^?3e$B}({(z>+L`O~rL~ru|l!A-(vP zx99m+QA(Q&uXib)zu0givN)5y9iG1u_)V5QA5p8#SkmX`4af&kN}KOJrobk{?d0P~ z_^!a8fIGA7`4q16yHa`SnIpaH>_Ao(b3qu==C(ZdzY+LSB|xW<@V6n=E3V3c@N-H@ z>6w1T&(3kb4Ix}hA$2Od0RN%nPP>$ly(6CgCeq9zTR*UQrAB!3q!;VGCWk#u$J*$^ z9GZ|A@y0Cozy-d>^G7C}@DqWrfeBgmYyzg>I=?N&Iei$Dxj6)<*PRMsrB4^Z`H33X zzCgCn*sj2Pc>bq&e!ql~{W?7V3ncG1TRWs0;Cfu=HA)obNiW$oK{XZkM<{L9s&hI7 zTupA(i3bCF@%#_*{6PsI`$xbZa-78P2l}e{SIMF*q`_#XUb9^_dkisl<3-wht9gb0 z?f1&hdHyh9CAs&?I)JP?uPItIZE$KS~e?h_#GGS_~@;~#HHkpsn9(YJiz$*$olNu$1_5)w3H`xi`bk=Si zLhdf=r-zz!_d$|R3dK5LZU{}ME$MS4<|y#nm($+`CGr{y82Lp(XPz*!@UbQUoty%3@jNp5fgJQ9;|oz=0DHWsNs zaZ={QvJD4L1)SF^H3!HfZ88zQQBCqJEMiD{A^s_(Pu1Bh#Gn41?YelGZAtEJYEcTL z7vdjLUXUP7`#hfi*aiPL(`doMYWgBi`uZUenL*KmuOY2rzST&ZAJ_?@MRG`=JMqtu zIW%B1u5*hWPK)@pPz+9$l{OaXA-xd)0Pr*N>*Uvnz6X|tMa)TmesbFv z$hA?!Hml_=l1+Lc{%K|Lx8$(5l{OYBAiWU34(Z|al$ACXCQ+7%vy|+sl}P-`g|J>5 zixgS>|!rSlJ4X=70uq`!EA`!W)cwv%vbVp>>OSXfwCSXfwCSXfwCSXfwCSXfwC xSXfwCSXfwCSXfwCSXfwCSXfwCSXiVV{|BA;A-ma7p``!-002ovPDHLkV1kH}K}`Sv literal 0 HcmV?d00001 diff --git a/web/public/images/pay.jpg b/web/public/images/pay.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef6f98d534356f7ee44a0af9ae8664b628688e1b GIT binary patch literal 72144 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 lUgK4TasYN zypFP&wX3e|k;B}ES~+F{2dUv<*9eX)Da>lr2iDG2t=^^@c~9=J^(Q&GL#o2k5k2;L!F-=eha0>6*et5cYM37gIw|!CjlQXtP%% z7DZe}a$~fDfvFWUwjCEH^6PgV7-5FUUMaQ$VY$@y$o0 zdv+o-%h}qa@~hN&Zc@3y+_mcEyAk|a-#7Jj18~84*2(n6%k6YwbY)SUHEfikeOo6~ z&FAGz%rykvjqZhx1%o8-{ZlH-m~{@7@#hW^FBiCv zT%VwPRp~gUJ4u1MGh=nmVT^=92sOL-D$PllF`gyKigHDqh`b*bwLlaOb!t{X1rG)} zi=!c&(9NrjB3cd-X{528CHck(o_F$gcju24qn=M4IAya83i^4yrhgm@I`1|Y7(qSrloQZb|3DcjVW3KE8h4|k^+`REwJbLF_HQD zH@9TMmSvf?cNWZ2v6<#(u5asNOgAmSed7L(7E{lz2a4w)q%FrK-Nf)~ltit0eX!#3}mU zwed=v-YGhX{hst}j3m+7E6zmka!t7k0{G@Khml&l8k?Kyk;l|L$|~RM!dSlbJHIz} zMqKW;J@goANaDUg>ga$+#z1+ zIlpH9pn{){Zw1%9*j&{U5whdnOS@$rqE=Md(fOkHZAtzW;0`Jwp(Tk|^2{3a77Q{4 z>V%-=4te=I^mlx@mNCMpSC`a0-ivV!+YcpR)t~Je&8S+vN;@+%mNQU1Yu-xM>e5l_XKU6J?m61t}RIi*B0PVNxRDP2;V&02t+Fl+Z z8u!Q~D$!H-a;?dWG7Wonw%43}2o~OW2tKo&l(;FbQ>`szXs$B;)KX}|68ysI(EtZM z>vJ+*w%p3X>T6pvD!^X6;T!i@m=g8I*|ASzU+w7yRs#!;QyoV7Z*>lYbLx$4SK@$6 z(KXiOy-fU~lYe^E;jy8Eg-J{#g09>EUd7?A2?^v(JmsxqISzjH!NyYsA!elnfLM)qtv-{7yDHnlwtdti!S_AFiX0Hb8=BXf7=E>4 zQ)_Zvo19+5E({eQ@@&42Cg->G&K?rA>_F^Sx={%t*R+~}EO{`&;9 zyzE%S87Oa9({>=`niXl-E1L2C{2SF7MBuI8=660DnRq|GbPnfPQwn>5aByKwn||b3 zPeWd}WFi~y#BIH1Z$b*I^G5ZGs&sY)MnAe}qmh>+bz;(|t@V&5_CeIln8S&pVYK7o z(u`#dR4B!_VIdt%Q5j`3nl!?hR@B9CzaO)|JIxKS21fvAvQU_ZEemo#aMRywttf7k zwDzQOFg;SLy9MaJUGHNrX@hR9xm>gMt?N)v@KduOe0WFy!(Uz2Qjk&VC6QDG=S_0k zLyz8EUmaWv@(&3K)bTrKhh`=?i+TUvNWykS;%{HQDi9*f7#9ZI&}4mEFWFBg7!nzH3yVBae_au9TheWD*%;?Z}kS#BH`6{4L)ba@GJPG?)wOBE*GgjsnSqNrl zFlG0T?F|fG|7KglQu;iaP^p2f#TGVdP50j%Sp0mNs^T0)U*pMc%@=#y zV3@rrnmtgmT{LA!<&;V$_LH8@v3Nn&ZQ;BQu{OvJjiJY$Z}mqRk=)3B-jXa*VG$|v zmhp0sXNPVqe$3>9s+(M1?qdzCQY`u+1O|&hAO{ZQ^r8?&HtGV}o(_B?q5QHrkE}i` zsHsway?jt;d-Wbh$U!VZO;${$8#=MZD(1QrCP$m%$bz{ktLAY?h)&(c?!D-#So8SR zn7|YpuOTOPiH;FXl)0iGso8QO@6o?g)!0a04n&;kD${#1MPX*bPo=h?KU(pYz(iK5 zl^Vpma|rBFBIdWzeeIj#(CQk3&hbpi-G=xAhlezfxuCSjvE6<>`5xgkX+yXI|-T`O*~5+|-^k@kY-SH$a>yc$Yele0&v-u%K4P z6-FrEpUuVZ*=>wrR&qZZ;yJAk9cu6Qtv3bbwB?ypoSEm6t4yuURK~oADY3*%G^ud; zP~CZXhJ9Sp*A7|}8$M_i7;NNrA@9RXik*m2)apdaXGyFTob)9@f*6b9;JVZqqng=3 z59wxCE*pz+vYF$NRkPB&e(AH5z@hWpKwx#6guhCjMw;exvZibw-`x-o_El56AE3Sw z7txCOKliYy+Uo1aUn5ziugNC`m~(Mh66`YbU}8UPdIT`w1i9KLS|j6pYnTNT7AfJ< zbTzI>kddu~EvYm9m1$$*D!B;csE@}YULtyMR4k5OXB7e;eOW{W_+0xw#sZ@)2xFI7 zb{z0?`Y3aE7AA;V(IZ@){ibgv>UBSvK`Yz{_pwwBxXLY|td$2OY9(BNjp_+gy^%K1 z|J(dqIM=G?DHV90%ip~s|54dIbj=KD{l;^XR;RR+Ir#B=T(dl>J8l7i^>=e@}F#< zK>8br5(4ee2o-F+bM~wTAe*l3a>nh7qP#w6V|+@9F-6Rp=bjHM>K%6cz38n z%U9j7t`&?XUo6;-GddIh?^CUvZxR>K@wQ(V_17l?xQCC}&#>F{lce&Pisk5qV*;T0tzuz=` z>@%{J8hK1D7JOY`fK|@>7uP#S8uJ7Td`IU8Ka~p(>w7hW^725MiV1VR;RJTl>}oYY znOS0c<&)DoujcDZpuR%#Ke69C(if@C3Cdu`-Im6fA6MC`uL}jPvw#a(be zGIYezfnigZe4PttROP*YFZr7oY&gnDAbw#ukJgkXmu*wXj6yU&D*VQ>Iof`PC*CaT z-WBaIdUk`ht$C}8Bpl3s{HSQBdRPO3=(NAGa})qjr)*3Froz`QBdU3|bDsr;6TH0z zJ}=ZprYL#p<6@u>CweB!g$iQHGc1LPk9(P8_-=;=4{t0#K5<%C?)t4Bt)m^Vt5~_Q zh1mN3o7CAN9^Lb7=O8oyojC_ENKVFYH`cO~vv=DY0fP;oYa|*v3k;PIfk9;#h$BWJ ziIO*6o1r9wQz|RrrO%L_C9)>>{68VLoCq$P%#ZxDL~&S=8=^5MlPI7q<@9OfQYvx~ zxJ;87ps4s>WC9@E)UHnm{6z(wv`{Z80(R{SrT!}NrEh+!yyBC5ooi9oU1rht_*o4s zbj0$@3zw+{?$ltx*VaNcHfjx?5@yR3yb61&s$A%HIR4o*bk*(RFHJ5>m@B)dKL74h zCXBepKnCMN#LSgDP`jryn>>cGV$#=jazu{GBITjDo$7R!{lwz6v(_in?^M11eDn_- zc}@PA5M_6dvhhiI|K$XH>v_f2*2Wh1hYz6@k^Cjxu4`gvv?xQgy1p16RD-Y7w9U{I zr%Qg~)Qy)ZB!_z-IzjGno)O0wX058^(TC5r!E-qRdan$O1CwpyML*qfNi^q~Rddm1 zrn{A-kswp#V1W$~LkQZB?P+Z6Nt3yJ5#IX&he}AlyZWZTVPe z)?uUPjqqA{jn3C6e5Lb@SwC3={?MTARK+R@=d~m}jWSS@2z<1xu;X1fhbmm1!U<`) zHkV=GN-};;Z=LQ&G4me4-s+u?Dvef~6hH|b;!>Qtoi0(S-KPm#0qpN|&Sv8vQ3&ir_xlDxWn$&0rTp{99mqtfv?xHTMuHb>O{ zYR@PMQeQSLe4br+?^g_$&X_Q_OY4=0w7w_$LvmUOzJ6 zD{5ogbAwVW?~tJKbgmfGi`56=#qO5b!nE@(SLJN*Hpk-H@`)N*DMwvrUx%e#Ysj^N zncZyqkZ{ROgk$@ytL>i|S(?*dHDgUDYOYz(5~vMh7GEtk=W(#TOT05~ zcc@mAZCG7DHs`8FkDiDhc&96C)^;&i1pk6Jle;!VN$i)TUbDO}7?}{C3)uy5;hXkZ z=E3AaFC=w=wyUm^@0TttciG7B zqqyTQvW{{CRi8^$NvcF>KHhhQL>6doK#9p}lr2MLS?{q=&>nE&1 z#qW~-biP3t15xlzhrn$q4M8CzTSY0jrlPoyF{)irSeACA&RPH>;@T9{k@ThQVf~*o zci$Pwk%n$ROFK-g935W&JY%bUUVxb|>W-CBsV~z!wS4?Sbiv)HR6zx|ysrbyQ)1lp zZZ4InT}(f;qx~I0FPry|?20wtf<`I});dIF^NgM%sag^Xy)X4TnB|p}SCVy=)@MU2 zz}ZoTiSKZ2^~FFJ0ycTIG%J=fCni}+JKy(edxAH+UT{Rpr@|kj(mFIf0?e*D@vHSL z+*RMGKBW@v)!B85&gF}I2Tp8I)RldgT*jdzdzax3*g-$va84fmqnfO=GDKJ0nI`Z} zG1Ygs!~2_7H@a?(V~?D@(hh>GznHlabd%XK>qmrekrkE0l}^qvvXk=wMAUB1Vqt9W z&*Ip}BNgxnkp3jSSnGxc`=dYy7M`f=Q3bU1>5;a2lG-IY*)g@ z!{!IE;fD$CVl0Vt_t%;(BYfr`jW`9oW}hqZp?P9vZ>A_nr0d^5@1b&*j+RQYu|5XI z?zaf${uW8k!?V;$ebW3_Xo>tQnS}qfo+3}D{*j0Qmi;$epV6u3-%k&Mo%8bD`-4?z zqYG}4Rp=0fhS3FkZ(P67rC*>#jRU^u$R&y@gfy1EXIFGeC1?)9-#ewkWpD^XR(fzO zewL6_k$5GiZ^)1P5aLr{PmR|3SIJBu=`oI3& zKpW(6+<-`EA~At@gXcN(u?V0iB?CJ%unY-$@DcxPQk4y|7idbF$F9dIl_KPR+f99( z_Oe0hXXu|+eTu~=$TIMiL0Pow8WP_HJi?hxf%SCtkd8?0NB@6lZsb2k?ZdRw$DA6= zp;BpZ=Wk@^f9bL>HBNn>{}P4D|9zC?UjYx8od2i;EBOnR0A%*R%E+z(EW*8i5tx7H z?*Hk-e+Yo5-~TbA&3csgRsuh(#tzM1_b_Co%6qIxpF#T)BDj5k+!h4w#y}RiB!a$B zlu6s)_vQvpsd__Zzd~jOfZvyZ>->ivnU*K1_J3RC)&Im0m9kh#Gihi)v7Prw)jC4y z^=B&CuQCw8{u?`#3j{lHWakjo&=}33%qdj~=nElJ{g8;P6ln!s5yNVmX^bDE$e4o& zH&3bX&jhiE35XD6DHNEeqHw@N_;Jt|(C;Hb>#8$8G)E+43AmY{Pf3F80S}0IWjd?= zo8tO)4M-ERJIK9W0s15_5h8kv6L=TLYm5NV{C_xHpB_w_qe4S7TTmDHY*Y$L?2-yE zl4&ZC6HUOnmfq{Ts1Ze#9U>a*U}MiI|N5cRH^8^DUwPaByiRW+B^CW>9lGxhSiItO z$)Yk!$N#PT{lCFuI>6gd<~>Y`gs;YhLvYy%N3uvDhWtPHXDE#bfg8_9`vbDa0hl1n z*0YH>=EtP}(FjF$AT9)J|Hl4}G-uLjC?S*RwOKD92BaT5{i78<9oYo^zCd>L0ID}f z{P8K(KN>&(kJnE*@}j_qA|a^1(TMsv=%0;G|7RmW*N}q{?j28Hkf5Be218D%{?TvZ z!T)H6{lYM@Xu0hg^l!}iR~NbQ+wS`W<1P zq1q1)=n@2$1@+&x@xNv%p#P8j`!c`}0lpCm9vH@3V-!yDSJ%R^nUrBNHsoLKLZ`R} zz>kwn*=@YSK>sGe0Z_|YHJPI+sfsYfcVXR`N@6TXfv%g8i#Ck_B zWoM(erIP>$M)LrPmLQ)9ufuhP@^^_k)trDFc2*INc6u$E%`{x5TTb zR2i|SR39p}PpRttPN|9`suhSJV29?_I;Aq&rsNiM*~Jr~NZdP!SlzIoYZ`X4@@BfL zScnH#?ekTMbK@4X*-;)oHr%9d;lo)P6SaNI1n

`bvaIksEuBS{0`zo4<47XU8RJ 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⋘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 VwCDzQxz6rMHuL>|Yp?!g;&0YC;8g$s literal 0 HcmV?d00001 diff --git a/web/yarn.lock b/web/yarn.lock new file mode 100644 index 0000000..8f6797a --- /dev/null +++ b/web/yarn.lock @@ -0,0 +1,727 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/autocomplete-core@1.7.1": + version "1.7.1" + resolved "https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.1.tgz#025538b8a9564a9f3dd5bcf8a236d6951c76c7d1" + integrity sha512-eiZw+fxMzNQn01S8dA/hcCpoWCOCwcIIEUtHHdzN5TGB3IpzLbuhqFeTfh2OUhhgkE8Uo17+wH+QJ/wYyQmmzg== + dependencies: + "@algolia/autocomplete-shared" "1.7.1" + +"@algolia/autocomplete-preset-algolia@1.7.1": + version "1.7.1" + resolved "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.1.tgz#7dadc5607097766478014ae2e9e1c9c4b3f957c8" + integrity sha512-pJwmIxeJCymU1M6cGujnaIYcY3QPOVYZOXhFkWVM7IxKzy272BwCvMFMyc5NpG/QmiObBxjo7myd060OeTNJXg== + dependencies: + "@algolia/autocomplete-shared" "1.7.1" + +"@algolia/autocomplete-shared@1.7.1": + version "1.7.1" + resolved "https://registry.npmmirror.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.1.tgz#95c3a0b4b78858fed730cf9c755b7d1cd0c82c74" + integrity sha512-eTmGVqY3GeyBTT8IWiB2K5EuURAqhnumfktAEoHxfDY2o7vg2rSnO16ZtIG0fMgt3py28Vwgq42/bVEuaQV7pg== + +"@algolia/cache-browser-local-storage@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.2.tgz#d5b1b90130ca87c6321de876e167df9ec6524936" + integrity sha512-FRweBkK/ywO+GKYfAWbrepewQsPTIEirhi1BdykX9mxvBPtGNKccYAxvGdDCumU1jL4r3cayio4psfzKMejBlA== + dependencies: + "@algolia/cache-common" "4.14.2" + +"@algolia/cache-common@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/cache-common/-/cache-common-4.14.2.tgz#b946b6103c922f0c06006fb6929163ed2c67d598" + integrity sha512-SbvAlG9VqNanCErr44q6lEKD2qoK4XtFNx9Qn8FK26ePCI8I9yU7pYB+eM/cZdS9SzQCRJBbHUumVr4bsQ4uxg== + +"@algolia/cache-in-memory@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/cache-in-memory/-/cache-in-memory-4.14.2.tgz#88e4a21474f9ac05331c2fa3ceb929684a395a24" + integrity sha512-HrOukWoop9XB/VFojPv1R5SVXowgI56T9pmezd/djh2JnVN/vXswhXV51RKy4nCpqxyHt/aGFSq2qkDvj6KiuQ== + dependencies: + "@algolia/cache-common" "4.14.2" + +"@algolia/client-account@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/client-account/-/client-account-4.14.2.tgz#b76ac1ba9ea71e8c3f77a1805b48350dc0728a16" + integrity sha512-WHtriQqGyibbb/Rx71YY43T0cXqyelEU0lB2QMBRXvD2X0iyeGl4qMxocgEIcbHyK7uqE7hKgjT8aBrHqhgc1w== + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/client-search" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-analytics@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/client-analytics/-/client-analytics-4.14.2.tgz#ca04dcaf9a78ee5c92c5cb5e9c74cf031eb2f1fb" + integrity sha512-yBvBv2mw+HX5a+aeR0dkvUbFZsiC4FKSnfqk9rrfX+QrlNOKEhCG0tJzjiOggRW4EcNqRmaTULIYvIzQVL2KYQ== + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/client-search" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-common@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/client-common/-/client-common-4.14.2.tgz#e1324e167ffa8af60f3e8bcd122110fd0bfd1300" + integrity sha512-43o4fslNLcktgtDMVaT5XwlzsDPzlqvqesRi4MjQz2x4/Sxm7zYg5LRYFol1BIhG6EwxKvSUq8HcC/KxJu3J0Q== + dependencies: + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-personalization@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/client-personalization/-/client-personalization-4.14.2.tgz#656bbb6157a3dd1a4be7de65e457fda136c404ec" + integrity sha512-ACCoLi0cL8CBZ1W/2juehSltrw2iqsQBnfiu/Rbl9W2yE6o2ZUb97+sqN/jBqYNQBS+o0ekTMKNkQjHHAcEXNw== + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-search@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/client-search/-/client-search-4.14.2.tgz#357bdb7e640163f0e33bad231dfcc21f67dc2e92" + integrity sha512-L5zScdOmcZ6NGiVbLKTvP02UbxZ0njd5Vq9nJAmPFtjffUSOGEp11BmD2oMJ5QvARgx2XbX4KzTTNS5ECYIMWw== + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/logger-common@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/logger-common/-/logger-common-4.14.2.tgz#b74b3a92431f92665519d95942c246793ec390ee" + integrity sha512-/JGlYvdV++IcMHBnVFsqEisTiOeEr6cUJtpjz8zc0A9c31JrtLm318Njc72p14Pnkw3A/5lHHh+QxpJ6WFTmsA== + +"@algolia/logger-console@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/logger-console/-/logger-console-4.14.2.tgz#ec49cb47408f5811d4792598683923a800abce7b" + integrity sha512-8S2PlpdshbkwlLCSAB5f8c91xyc84VM9Ar9EdfE9UmX+NrKNYnWR1maXXVDQQoto07G1Ol/tYFnFVhUZq0xV/g== + dependencies: + "@algolia/logger-common" "4.14.2" + +"@algolia/requester-browser-xhr@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.2.tgz#a2cd4d9d8d90d53109cc7f3682dc6ebf20f798f2" + integrity sha512-CEh//xYz/WfxHFh7pcMjQNWgpl4wFB85lUMRyVwaDPibNzQRVcV33YS+63fShFWc2+42YEipFGH2iPzlpszmDw== + dependencies: + "@algolia/requester-common" "4.14.2" + +"@algolia/requester-common@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/requester-common/-/requester-common-4.14.2.tgz#bc4e9e5ee16c953c0ecacbfb334a33c30c28b1a1" + integrity sha512-73YQsBOKa5fvVV3My7iZHu1sUqmjjfs9TteFWwPwDmnad7T0VTCopttcsM3OjLxZFtBnX61Xxl2T2gmG2O4ehg== + +"@algolia/requester-node-http@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/requester-node-http/-/requester-node-http-4.14.2.tgz#7c1223a1785decaab1def64c83dade6bea45e115" + integrity sha512-oDbb02kd1o5GTEld4pETlPZLY0e+gOSWjWMJHWTgDXbv9rm/o2cF7japO6Vj1ENnrqWvLBmW1OzV9g6FUFhFXg== + dependencies: + "@algolia/requester-common" "4.14.2" + +"@algolia/transporter@4.14.2": + version "4.14.2" + resolved "https://registry.npmmirror.com/@algolia/transporter/-/transporter-4.14.2.tgz#77c069047fb1a4359ee6a51f51829508e44a1e3d" + integrity sha512-t89dfQb2T9MFQHidjHcfhh6iGMNwvuKUvojAj+JsrHAGbuSy7yE4BylhLX6R0Q1xYRoC4Vvv+O5qIw/LdnQfsQ== + dependencies: + "@algolia/cache-common" "4.14.2" + "@algolia/logger-common" "4.14.2" + "@algolia/requester-common" "4.14.2" + +"@babel/parser@^7.16.4": + version "7.18.11" + resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" + integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== + +"@docsearch/css@3.2.1", "@docsearch/css@^3.2.1": + version "3.2.1" + resolved "https://registry.npmmirror.com/@docsearch/css/-/css-3.2.1.tgz#c05d7818b0e43b42f9efa2d82a11c36606b37b27" + integrity sha512-gaP6TxxwQC+K8D6TRx5WULUWKrcbzECOPA2KCVMuI+6C7dNiGUk5yXXzVhc5sld79XKYLnO9DRTI4mjXDYkh+g== + +"@docsearch/js@^3.2.1": + version "3.2.1" + resolved "https://registry.npmmirror.com/@docsearch/js/-/js-3.2.1.tgz#d6856fb6223c7a47091640264d5144d59806bc30" + integrity sha512-H1PekEtSeS0msetR2YGGey2w7jQ2wAKfGODJvQTygSwMgUZ+2DHpzUgeDyEBIXRIfaBcoQneqrzsljM62pm6Xg== + dependencies: + "@docsearch/react" "3.2.1" + preact "^10.0.0" + +"@docsearch/react@3.2.1": + version "3.2.1" + resolved "https://registry.npmmirror.com/@docsearch/react/-/react-3.2.1.tgz#112ad88db07367fa6fd933d67d58421d8d8289aa" + integrity sha512-EzTQ/y82s14IQC5XVestiK/kFFMe2aagoYFuTAIfIb/e+4FU7kSMKonRtLwsCiLQHmjvNQq+HO+33giJ5YVtaQ== + dependencies: + "@algolia/autocomplete-core" "1.7.1" + "@algolia/autocomplete-preset-algolia" "1.7.1" + "@docsearch/css" "3.2.1" + algoliasearch "^4.0.0" + +"@esbuild/linux-loong64@0.14.54": + version "0.14.54" + resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" + integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== + +"@types/node@^18.7.8": + version "18.7.8" + resolved "https://registry.npmmirror.com/@types/node/-/node-18.7.8.tgz#6bbf2be6fbf9c187a5040d4277d24a06a18957a1" + integrity sha512-/YP55EMK2341JkODUb8DM9O0x1SIz2aBvyF33Uf1c76St3VpsMXEIW0nxuKkq/5cxnbz0RD9cfwNZHEAZQD3ag== + +"@types/web-bluetooth@^0.0.15": + version "0.0.15" + resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72" + integrity sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA== + +"@vitejs/plugin-vue@^3.0.3": + version "3.0.3" + resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.0.3.tgz#7e3e401ccb30b4380d2279d9849281413f1791ef" + integrity sha512-U4zNBlz9mg+TA+i+5QPc3N5lQvdUXENZLO2h0Wdzp56gI1MWhqJOv+6R+d4kOzoaSSq6TnGPBdZAXKOe4lXy6g== + +"@vue/compiler-core@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a" + integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/shared" "3.2.37" + estree-walker "^2.0.2" + source-map "^0.6.1" + +"@vue/compiler-dom@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5" + integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ== + dependencies: + "@vue/compiler-core" "3.2.37" + "@vue/shared" "3.2.37" + +"@vue/compiler-sfc@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4" + integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.37" + "@vue/compiler-dom" "3.2.37" + "@vue/compiler-ssr" "3.2.37" + "@vue/reactivity-transform" "3.2.37" + "@vue/shared" "3.2.37" + estree-walker "^2.0.2" + magic-string "^0.25.7" + postcss "^8.1.10" + source-map "^0.6.1" + +"@vue/compiler-ssr@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff" + integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw== + dependencies: + "@vue/compiler-dom" "3.2.37" + "@vue/shared" "3.2.37" + +"@vue/devtools-api@^6.2.1": + version "6.2.1" + resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz#6f2948ff002ec46df01420dfeff91de16c5b4092" + integrity sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ== + +"@vue/reactivity-transform@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca" + integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.37" + "@vue/shared" "3.2.37" + estree-walker "^2.0.2" + magic-string "^0.25.7" + +"@vue/reactivity@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848" + integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A== + dependencies: + "@vue/shared" "3.2.37" + +"@vue/runtime-core@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3" + integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ== + dependencies: + "@vue/reactivity" "3.2.37" + "@vue/shared" "3.2.37" + +"@vue/runtime-dom@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd" + integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw== + dependencies: + "@vue/runtime-core" "3.2.37" + "@vue/shared" "3.2.37" + csstype "^2.6.8" + +"@vue/server-renderer@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc" + integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA== + dependencies: + "@vue/compiler-ssr" "3.2.37" + "@vue/shared" "3.2.37" + +"@vue/shared@3.2.37": + version "3.2.37" + resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702" + integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw== + +"@vueuse/core@^9.1.0": + version "9.1.0" + resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.1.0.tgz#f0fb13fd99768c0eb617169a2d2c1cbd5f5a52eb" + integrity sha512-BIroqvXEqt826aE9r3K5cox1zobuPuAzdYJ36kouC2TVhlXvFKIILgFVWrpp9HZPwB3aLzasmG3K87q7TSyXZg== + dependencies: + "@types/web-bluetooth" "^0.0.15" + "@vueuse/metadata" "9.1.0" + "@vueuse/shared" "9.1.0" + vue-demi "*" + +"@vueuse/metadata@9.1.0": + version "9.1.0" + resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.1.0.tgz#194d4bd47f7acb91e348c0f436e678ddf7ee235b" + integrity sha512-8OEhlog1iaAGTD3LICZ8oBGQdYeMwByvXetOtAOZCJOzyCRSwqwdggTsmVZZ1rkgYIEqgUBk942AsAPwM21s6A== + +"@vueuse/shared@9.1.0": + version "9.1.0" + resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.1.0.tgz#d8459a45324f32fb05a2a56ed754637c3d0efaeb" + integrity sha512-pB/3njQu4tfJJ78ajELNda0yMG6lKfpToQW7Soe09CprF1k3QuyoNi1tBNvo75wBDJWD+LOnr+c4B5HZ39jY/Q== + dependencies: + vue-demi "*" + +algoliasearch@^4.0.0: + version "4.14.2" + resolved "https://registry.npmmirror.com/algoliasearch/-/algoliasearch-4.14.2.tgz#63f142583bfc3a9bd3cd4a1b098bf6fe58e56f6c" + integrity sha512-ngbEQonGEmf8dyEh5f+uOIihv4176dgbuOZspiuhmTTBRBuzWu3KCGHre6uHj5YyuC7pNvQGzB6ZNJyZi0z+Sg== + dependencies: + "@algolia/cache-browser-local-storage" "4.14.2" + "@algolia/cache-common" "4.14.2" + "@algolia/cache-in-memory" "4.14.2" + "@algolia/client-account" "4.14.2" + "@algolia/client-analytics" "4.14.2" + "@algolia/client-common" "4.14.2" + "@algolia/client-personalization" "4.14.2" + "@algolia/client-search" "4.14.2" + "@algolia/logger-common" "4.14.2" + "@algolia/logger-console" "4.14.2" + "@algolia/requester-browser-xhr" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/requester-node-http" "4.14.2" + "@algolia/transporter" "4.14.2" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-scroll-lock@^4.0.0-beta.0: + version "4.0.0-beta.0" + resolved "https://registry.npmmirror.com/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz#4f78789d10e6388115c0460cd6d7d4dd2bbc4f7e" + integrity sha512-a7tP5+0Mw3YlUJcGAKUqIBkYYGlYxk2fnCasq/FUph1hadxlTRjF+gAcZksxANnaMnALjxEddmSi/H3OR8ugcQ== + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +"chokidar@>=3.0.0 <4.0.0": + version "3.5.3" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +csstype@^2.6.8: + version "2.6.20" + resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" + integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA== + +esbuild-android-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" + integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== + +esbuild-android-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" + integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== + +esbuild-darwin-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" + integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== + +esbuild-darwin-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" + integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + +esbuild-freebsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" + integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== + +esbuild-freebsd-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" + integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== + +esbuild-linux-32@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" + integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== + +esbuild-linux-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652" + integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== + +esbuild-linux-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" + integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== + +esbuild-linux-arm@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" + integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== + +esbuild-linux-mips64le@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" + integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== + +esbuild-linux-ppc64le@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" + integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== + +esbuild-linux-riscv64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" + integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== + +esbuild-linux-s390x@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" + integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== + +esbuild-netbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" + integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== + +esbuild-openbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" + integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== + +esbuild-sunos-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" + integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== + +esbuild-windows-32@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" + integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== + +esbuild-windows-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" + integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== + +esbuild-windows-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" + integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg== + +esbuild@^0.14.47: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2" + integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== + optionalDependencies: + "@esbuild/linux-loong64" "0.14.54" + esbuild-android-64 "0.14.54" + esbuild-android-arm64 "0.14.54" + esbuild-darwin-64 "0.14.54" + esbuild-darwin-arm64 "0.14.54" + esbuild-freebsd-64 "0.14.54" + esbuild-freebsd-arm64 "0.14.54" + esbuild-linux-32 "0.14.54" + esbuild-linux-64 "0.14.54" + esbuild-linux-arm "0.14.54" + esbuild-linux-arm64 "0.14.54" + esbuild-linux-mips64le "0.14.54" + esbuild-linux-ppc64le "0.14.54" + esbuild-linux-riscv64 "0.14.54" + esbuild-linux-s390x "0.14.54" + esbuild-netbsd-64 "0.14.54" + esbuild-openbsd-64 "0.14.54" + esbuild-sunos-64 "0.14.54" + esbuild-windows-32 "0.14.54" + esbuild-windows-64 "0.14.54" + esbuild-windows-arm64 "0.14.54" + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +immutable@^4.0.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.9.0: + version "2.10.0" + resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +jsonc-parser@^3.0.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d" + integrity sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg== + +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +postcss@^8.1.10, postcss@^8.4.16: + version "8.4.16" + resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" + integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +preact@^10.0.0: + version "10.10.6" + resolved "https://registry.npmmirror.com/preact/-/preact-10.10.6.tgz#1fe62aecf93974b64e6a42e09ba1f00f93207d14" + integrity sha512-w0mCL5vICUAZrh1DuHEdOWBjxdO62lvcO++jbzr8UhhYcTbFkpegLH9XX+7MadjTl/y0feoqwQ/zAnzkc/EGog== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +"rollup@>=2.75.6 <2.77.0 || ~2.77.0": + version "2.77.3" + resolved "https://registry.npmmirror.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12" + integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g== + optionalDependencies: + fsevents "~2.3.2" + +sass@^1.54.5: + version "1.54.5" + resolved "https://registry.npmmirror.com/sass/-/sass-1.54.5.tgz#93708f5560784f6ff2eab8542ade021a4a947b3a" + integrity sha512-p7DTOzxkUPa/63FU0R3KApkRHwcVZYC0PLnLm5iyZACyp15qSi32x7zVUhRdABAATmkALqgGrjCJAcWvobmhHw== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +shiki@^0.11.1: + version "0.11.1" + resolved "https://registry.npmmirror.com/shiki/-/shiki-0.11.1.tgz#df0f719e7ab592c484d8b73ec10e215a503ab8cc" + integrity sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA== + dependencies: + jsonc-parser "^3.0.0" + vscode-oniguruma "^1.6.1" + vscode-textmate "^6.0.0" + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +vite@^3.0.8: + version "3.0.9" + resolved "https://registry.npmmirror.com/vite/-/vite-3.0.9.tgz#45fac22c2a5290a970f23d66c1aef56a04be8a30" + integrity sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw== + dependencies: + esbuild "^0.14.47" + postcss "^8.4.16" + resolve "^1.22.1" + rollup ">=2.75.6 <2.77.0 || ~2.77.0" + optionalDependencies: + fsevents "~2.3.2" + +vitepress@^1.0.0-alpha.9: + version "1.0.0-alpha.9" + resolved "https://registry.npmmirror.com/vitepress/-/vitepress-1.0.0-alpha.9.tgz#3426c8c6da2a63a811356f27e42fb9d521038aeb" + integrity sha512-+VxgjflIcssbuyUBRYjZRpQ8w25v6XnIbXFmm9qwLVaqZdC0rN6qViEp7oRwA3HUlrGxj7GEzqQjW7xj4+E83w== + dependencies: + "@docsearch/css" "^3.2.1" + "@docsearch/js" "^3.2.1" + "@vitejs/plugin-vue" "^3.0.3" + "@vue/devtools-api" "^6.2.1" + "@vueuse/core" "^9.1.0" + body-scroll-lock "^4.0.0-beta.0" + shiki "^0.11.1" + vite "^3.0.8" + vue "^3.2.37" + +vscode-oniguruma@^1.6.1: + version "1.6.2" + resolved "https://registry.npmmirror.com/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz#aeb9771a2f1dbfc9083c8a7fdd9cccaa3f386607" + integrity sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA== + +vscode-textmate@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/vscode-textmate/-/vscode-textmate-6.0.0.tgz#a3777197235036814ac9a92451492f2748589210" + integrity sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ== + +vue-demi@*: + version "0.13.8" + resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.8.tgz#5c568fb3b4d8f848acc658dfccd3d875035b5653" + integrity sha512-Vy1zbZhCOdsmvGR6tJhAvO5vhP7eiS8xkbYQSoVa7o6KlIy3W8Rc53ED4qI4qpeRDjv3mLfXSEpYU6Yq4pgXRg== + +vue@^3.2.37: + version "3.2.37" + resolved "https://registry.npmmirror.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e" + integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ== + dependencies: + "@vue/compiler-dom" "3.2.37" + "@vue/compiler-sfc" "3.2.37" + "@vue/runtime-dom" "3.2.37" + "@vue/server-renderer" "3.2.37" + "@vue/shared" "3.2.37"