小程序支付 接入 EasyWeChat(6.X)

环境需求
PHP >= 8.0
PHP cURL 扩展
PHP OpenSSL 扩展
PHP SimpleXML 扩展
PHP fileinfo 拓展
安装
composer require w7corp/easywechat:^6.17
开始

引入类库、定义变量

use EasyWeChat\MiniApp\Application as MiniApp;//小程序类
use EasyWeChat\Pay\Application as Payment;//微信支付类

protected MiniApp $miniApp;
protected Payment $payApp;
public array $routineConfig = [
    'app_id' => '',
    'app_secret' => '',
    'mch_id' => '',
    'secret_key' => '',
    'private_key' => '',
    'certificate' => '',
];
初始化小程序
public function initMiniApp(): bool
{
    try {
        $this->miniApp = new MiniApp([
            'app_id' => $this->routineConfig['app_id'],
            'secret' => $this->routineConfig['app_secret'],
            'token' => 'easywechat',
            'aes_key' => '......',
            // 下面为可选项
            // 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
            'response_type' => 'array',
            'log' => [
                'level' => 'debug',
                'file' => runtime_path() . '/wechat.log',
            ],
            /**
             * 是否使用 Stable Access Token
             * 默认 false
             * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html
             * true 使用 false 不使用
             */
            'use_stable_access_token' => false,
            'http' => [
                'verify' => false, // 禁用 SSL 证书验证
                'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启
                'timeout' => 5.0,
                // 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
                'retry' => true, // 使用默认重试配置
                //  'retry' => [
                //      // 仅以下状态码重试
                //      'status_codes' => [429, 500]
                //       // 最大重试次数
                //      'max_retries' => 3,
                //      // 请求间隔 (毫秒)
                //      'delay' => 1000,
                //      // 如果设置,每次重试的等待时间都会增加这个系数
                //      // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.)
                //      'multiplier' => 3
                //  ],
            ],
        ]);
        return true;
    } catch (InvalidArgumentException|\Throwable $e) {
        Log::error("\n".'小程序配置错误:'."\nerror:".$e->getMessage());
    }
    return false;
}
初始化微信支付
public function initPay(): bool
{
    try {
        $this->payApp = new Payment([
            'mch_id' => $this->communityInfo['mch_id'],
            'secret_key' => $this->communityInfo['secret_key'],// v3 API 秘钥
//                'v2_secret_key' => $info['mch_id'],//v2 API 秘钥
            // 商户证书
            'private_key' => public_path($this->communityInfo['private_key']),
            'certificate' => public_path($this->communityInfo['certificate']),
            'log' => [
                'level' => 'debug',
                'file' => runtime_path() . '/wechat.log',
            ],
            // 平台证书:微信支付 APIv3 平台证书,需要使用工具下载
            // 下载工具:https://github.com/wechatpay-apiv3/CertificateDownloader
            'platform_certs' => [
                (string)($this->communityInfo['pub_key_id']) => public_path($this->communityInfo['pub_key']),
                // 如果是「平台证书」模式
                //    可简写使用平台证书文件绝对路径
                // '/path/to/wechatpay/cert.pem',

                // 如果是「微信支付公钥」模式
                //    使用Key/Value结构, key为微信支付公钥ID,value为微信支付公钥文件绝对路径
                // "{$pubKeyId}" => '/path/to/wechatpay/pubkey.pem',
            ],
            /**
             * 接口请求相关配置,超时时间等,具体可用参数请参考:
             * https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php
             */
            'http' => [
                'verify' => false, // 禁用 SSL 证书验证
                'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启
                'timeout' => 5.0,
                // 'base_uri' => 'https://api.mch.weixin.qq.com/',// 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 base_uri
            ],
        ]);
        return true;
    } catch (InvalidArgumentException|\Throwable $e) {
        Log::error("\n".'小程序配置错误:'."\nerror:".$e->getMessage());
    }
    return false;
}
根据CODE获取OpenId
前端传过来的code
$code = $request->input('code','');
$this->initMiniApp()
$account = $this->miniApp->getAccount();
//获取appid
$account->getAppId();
$utils = $this->miniApp->getUtils();
根据code获取openid等
$res = $utils->codeToSession($code);
获取下单参数
use app\extend\SnowFlake;
public function orderPay(Request $request): Response
{
    if(!$this->initPay()){
        return $this->fail('支付初始化失败');
    }
    try {
        $domain = 'http://aaa.com/';
        $orderData = [
            "mchid" => $this->communityInfo['mch_id'], // <---- 请修改为您的商户号
            "out_trade_no" => SnowFlake::generateId(),
            "appid" => $this->communityInfo['app_id'], // <---- 请修改为服务号的 appid
            "description" => $this->communityInfo['pay_desc'],
            "notify_url" => $domain."routine/notify/vipNotify/".$this->communityInfo['communityid'],
            "amount" => [
                "total" => (int)bcmul($rechargeInfo['price'] , 100,0),
                "currency" => "CNY"
            ],
            "payer" => [
                "openid" => $userInfo['openid'] // <---- 请修改为服务号下单用户的 openid
            ]
        ];
        $res = $this->payApp->getClient()->postJson("v3/pay/transactions/jsapi", $orderData)->toArray();
        if(empty($res)){
            return $this->fail('微信支付异常');
        }
        $payData = $this->payApp->getUtils()->buildMiniAppConfig($res['prepay_id'], $this->communityInfo['app_id']); // 返回数组
        return $this->success($payData);
    } catch (\Throwable $e) {
        return $this->fail($e->getMessage());
    }
    return $this->fail();
}
 UNIAPP拉起支付
uni.requestPayment({
    provider: 'wxpay',
    timeStamp: res.data.timeStamp,
    nonceStr: res.data.nonceStr,
    package: res.data.package,
    signType: res.data.signType,
    paySign: res.data.paySign,
    success: function(res) {
        uni.showLoading({
            mask:true,
            title: "支付中"
        });
        setTimeout(() => {
            uni.hideLoading();
        }, 2000);
    },
    fail: function(err) {
        console.log('fail:' + JSON.stringify(err));
    }
});
支付回调
use EasyWeChat\Pay\Message;
use plugin\saiadmin\app\middleware\CrossDomain;
use support\annotation\Middleware;
use support\annotations\Annotations\RequestMapping;
use support\annotations\Annotations\RestController;
use support\Log;
use support\Request;
use support\Response;
use Symfony\Component\HttpFoundation\HeaderBag;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
#[Apidoc\Title("支付回调类")]
#[RestController("/routine/notify")]
#[Middleware(CrossDomain::class)]
class NotifyController extends BaseController
{
    #[Apidoc\Title("VIP支付回调")]
    #[Apidoc\Url("/routine/notify/vipNotify/444")]  // 保持与路由一致
    #[Apidoc\Method("POST,GET")]
    #[Apidoc\ParamType("formdata")]
    #[Apidoc\Param(name: "communityid", type: "string", desc: "小区ID", required: true)]
    #[RequestMapping(["/vipNotify/{communityid}"], methods: ['GET', 'POST', 'OPTIONS'])]
    public function notify(Request $request,$communityid): Response
    {
        try {
            if(!$communityid){
                Log::error('小区ID不能为空:'.$communityid);
                return $this->error('小区ID不能为空');
            }
            if(!$this->initPay()){
                Log::error('支付初始化失败:'.$communityid);
                return $this->fail('支付初始化失败');
            }
            $symfony_request = new SymfonyRequest( $request->get(),$request->post(),[],$request->cookie(),[],[],$request->rawBody() );
            $header =  $request->header();
            $symfony_request->headers = new HeaderBag( $header );
            $this->payApp->setRequestFromSymfonyRequest($symfony_request);
            $server = $this->payApp->getServer();
            $server->handlePaid(function (Message $message) {
                switch ($message->trade_state) {
                    case 'SUCCESS'://支付成功
                        // 建议是拿订单号调用微信支付查询接口,以查询到的订单状态为准
                        $res = $this->payApp->getClient()->get("v3/pay/transactions/out-trade-no/{$message->out_trade_no}", [
                            'query' => [
                                'mchid' => $this->payApp->getMerchant()->getMerchantId()
                            ]
                        ])->toArray();
//                        Log::info('建议是拿订单号调用微信支付查询接口,以查询到的订单状态为准', $res);
                        // 5. 严格状态校验
                        if (empty($res['trade_state']) || $res['trade_state'] !== 'SUCCESS') {
                            break;
                        }
                        //支付成功逻辑
                        (new UsersVipbillLogic())->paySuccess($res);
                        break;
                    case 'NOTPAY'://未支付
                        break;
                    case 'REFUND'://转入退款
                        break;
                    case 'CLOSED'://已关闭
                        break;
                }

            })->serve();
        } catch (\Throwable $e) {
            Log::info('支付回调验证失败' . $e->getMessage());
        }
        return $this->success();
    }
}

### EasyWeChat 6.x 版本中模板消息的使用 在EasyWeChat 6.x版本中,发送模板消息的功能被集成到了`OfficialAccount\Client`类下。为了成功发送一条模板消息,开发者需要准备如下几个要素: - 接收者openid - 模板ID - 跳转链接(可选) - 数据参数 #### 准备工作 首先,在项目根目录执行Composer命令来安装最新版的EasyWeChat SDK[^2]。 ```bash composer require overtrue/wechat:~6.0 ``` 接着,在Laravel框架内完成服务提供者的注册以及配置文件发布操作[^1]。 #### 发送模板消息实例 下面是一个简单的例子展示如何通过EasyWeChat向指定用户发送模板消息: ```php use EasyWeChat\Factory; $app = Factory::officialAccount(config('wechat.official_account.default')); $client = $app->template_message; $messages = [ 'touser' => 'OPENID', // 用户OpenId 'template_id' => 'TEMPLATE_ID', // 微信后台审核通过后的模板ID 'url' => 'http://weixin.qq.com/download', // 可选跳转链接 'data' => [ 'first' => ['value' => '您好,您有一条新的订单通知'], 'keyword1' => ['value' => '订单号:123456789'], 'keyword2' => ['value' => date('Y-m-d H:i:s')], 'remark' => ['value' => '感谢您的支持'] ] ]; try { $result = $client->send($messages); } catch (\Exception $exception) { echo "Error Message:" . $exception->getMessage(); } ``` 这段代码展示了创建一个官方账号应用对象,并调用其下的`template_message`属性获取客户端实例,最后构建并尝试发送带有特定数据结构的消息体给定目标用户。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值