> 백엔드 개발 > PHP 튜토리얼 > WeChat 결제가 업그레이드되면서 PHP WeChat 결제 V3 인터페이스도 출시되었습니다.

WeChat 결제가 업그레이드되면서 PHP WeChat 결제 V3 인터페이스도 출시되었습니다.

藏色散人
풀어 주다: 2023-04-10 11:16:02
앞으로
5072명이 탐색했습니다.

무의식적으로 WeChat 결제도 업데이트되었으며, 인터페이스 버전도 V3로 업그레이드되었습니다. WeChat 업그레이드에 이어 WeChat 결제의 개인 사용도 V3 WeChat 결제 문서로 업그레이드되었습니다. weixin.qq.com /wiki/doc/apiv3/index.shtml.

사용 방법은 이전과 동일합니다(V2 WeChat 결제). 매개변수를 직접 전달하여 사용할 수 있습니다.

새 버전에는 통합 프레임워크(Github 주소)의 사용을 용이하게 하기 위해 composer설치가 추가되었습니다.

1

composer require fengkui/pay

로그인 후 복사

먼저 구성 파일을 채워주세요. (잘못 입력하지 않도록 주의하세요. 그렇지 않으면 서명 오류가 발생할 수 있습니다.):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

# 微信支付配置

$wechatConfig = [

    'xcxid'         => '', // 小程序 appid

    'appid'         => '', // 微信支付 appid

    'mchid'         => '', // 微信支付 mch_id 商户收款账号

    'key'           => '', // 微信支付 apiV3key(尽量包含大小写字母,否则验签不通过)

    'appsecret'     => '', // 公众帐号 secert (公众号支付获取 code 和 openid 使用)

 

    'notify_url'    => '', // 接收支付状态的连接  改成自己的回调地址

    'redirect_url'  => '', // 公众号支付,调起支付页面

 

    'serial_no'     => '', // 证书序列号

    'cert_client'   => './cert/apiclient_cert.pem', // 证书(退款,红包时使用)

    'cert_key'      => './cert/apiclient_key.pem', // 商户私钥(Api安全中下载)

    'public_key'    => './cert/public_key.pem', // 平台公钥(调动证书列表,自动生成)

];

로그인 후 복사

결제 클래스 패키지 관련 방법:

methodDescription
jsJSAPI 주문
app APP 결제
h5H5 결제
scanNavicat 결제
xcx미니 프로그램 결제
query 주문조회
close주문닫기
refund환불신청
notify결제결과알림

사용법( 여기서는 미니 프로그램 결제를 예시로 사용했습니다.) :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<?php

require_once(&#39;./vendor/autoload.php&#39;);

 

$config = []; // 支付配置

$order = [

    &#39;order_sn&#39; => time(), // 订单编号

    &#39;total_amount&#39; => 1, // 订单金额(分)

    &#39;body&#39; => &#39;测试商品&#39;, // 商品名称

    &#39;openid&#39; => &#39;&#39;, // 用户openid

    // &#39;type&#39; => &#39;Wap&#39;,

];

 

$wechat = new fengkui\Pay\Wechat($config);

$re = $wechat->xcx($order);

die(json_encode($re)); // JSON化直接返回小程序客户端

로그인 후 복사

다음 코드는 캡슐화된 전체 결제 클래스 파일(Wechat.php)입니다.
필요에 따라 자유롭게 수정할 수 있습니다. 자세한 사용 방법은 나중에 문서화됩니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

<?php

/**

 * @Author: [FENG] <1161634940@qq.com>

 * @Date:   2019-09-06 09:50:30

 * @Last Modified by:   [FENG] <1161634940@qq.com>

 * @Last Modified time: 2021-07-12 18:24:18

 */

namespace fengkui\Pay;

 

use Exception;

use RuntimeException;

use fengkui\Supports\Http;

 

/**

 * Wechat 微信支付

 * 新版(V3)接口(更新中)

 */

class Wechat

{

    const AUTH_TAG_LENGTH_BYTE = 16;

 

    // 新版相关接口

    // GET 获取平台证书列表

    private static $certificatesUrl = &#39;https://api.mch.weixin.qq.com/v3/certificates&#39;;

    // 统一下订单管理

    private static $transactionsUrl = &#39;https://api.mch.weixin.qq.com/v3/pay/transactions/&#39;;

    // 申请退款

    private static $refundUrl = &#39;https://api.mch.weixin.qq.com/v3/refund/domestic/refunds&#39;;

    // 静默授权,获取code

    private static $authorizeUrl = &#39;https://open.weixin.qq.com/connect/oauth2/authorize&#39;;

    // 通过code获取access_token以及openid

    private static $accessTokenUrl = &#39;https://api.weixin.qq.com/sns/oauth2/access_token&#39;;

 

    // 支付完整配置

    private static $config = array(

        &#39;xcxid&#39;         => &#39;&#39;, // 小程序appid

        &#39;appid&#39;         => &#39;&#39;, // 微信支付appid

        &#39;mchid&#39;         => &#39;&#39;, // 微信支付 mch_id 商户收款账号

        &#39;key&#39;           => &#39;&#39;, // 微信支付 apiV3key(尽量包含大小写字母,否则验签不通过)

        &#39;appsecret&#39;     => &#39;&#39;, // 公众帐号 secert (公众号支付获取code 和 openid使用)

 

        &#39;notify_url&#39;    => &#39;&#39;, // 接收支付状态的连接  改成自己的回调地址

        &#39;redirect_url&#39;  => &#39;&#39;, // 公众号支付,调起支付页面

 

        &#39;serial_no&#39;     => &#39;&#39;, // 证书序列号

        &#39;cert_client&#39;   => &#39;./cert/apiclient_cert.pem&#39;, // 证书(退款,红包时使用)

        &#39;cert_key&#39;      => &#39;./cert/apiclient_key.pem&#39;, // 商户私钥(Api安全中下载)

        &#39;public_key&#39;    => &#39;./cert/public_key.pem&#39;, // 平台公钥(调动证书列表,自动生成)

    );

 

    /**

     * [__construct 构造函数]

     * @param [type] $config [传递微信支付相关配置]

     */

    public function __construct($config=NULL, $referer=NULL){

        $config && self::$config = array_merge(self::$config, $config);

    }

 

    /**

     * [unifiedOrder 统一下单]

     * @param  [type]  $order [订单信息(必须包含支付所需要的参数)]

     * @param  boolean $type  [区分是否是小程序,是则传 true]

     * @return [type]         [description]

     * $order = array(

     *      &#39;body&#39;         => &#39;&#39;, // 产品描述

     *      &#39;order_sn&#39;     => &#39;&#39;, // 订单编号

     *      &#39;total_amount&#39; => &#39;&#39;, // 订单金额(分)

     * );

     */

    public static function unifiedOrder($order, $type=false)

    {

        $config = array_filter(self::$config);

 

        // 获取配置项

        $params = array(

            &#39;appid&#39;         => $type ? $config[&#39;xcxid&#39;] : $config[&#39;appid&#39;], // 由微信生成的应用ID

            &#39;mchid&#39;         => $config[&#39;mchid&#39;], // 直连商户的商户号

            &#39;description&#39;   => $order[&#39;body&#39;], // 商品描述

            &#39;out_trade_no&#39;  => (string)$order[&#39;order_sn&#39;], // 商户系统内部订单号

            &#39;notify_url&#39;    => $config[&#39;notify_url&#39;], // 通知URL必须为直接可访问的URL

            &#39;amount&#39;        => [&#39;total&#39; => (int)$order[&#39;total_amount&#39;], &#39;currency&#39; => &#39;CNY&#39;], // 订单金额信息

        );

 

        !empty($order[&#39;attach&#39;]) && $params[&#39;attach&#39;] = $order[&#39;attach&#39;]; // 附加数据

        if (!empty($order[&#39;time_expire&#39;])) { // 订单失效时间

            preg_match(&#39;/[年\/-]/&#39;, $order[&#39;time_expire&#39;]) && $order[&#39;time_expire&#39;] = strtotime($order[&#39;time_expire&#39;]);

            $time = $order[&#39;time_expire&#39;] > time() ? $order[&#39;time_expire&#39;] : $order[&#39;time_expire&#39;] + time();

            $params[&#39;time_expire&#39;] = date(DATE_ATOM, $time);

        }

 

        if (!in_array($order[&#39;type&#39;], [&#39;native&#39;])) {

            !empty($order[&#39;openid&#39;]) && $params[&#39;payer&#39;] = [&#39;openid&#39; => $order[&#39;openid&#39;]];

            $params[&#39;scene_info&#39;] = [&#39;payer_client_ip&#39; => self::get_ip()];

        }

 

        if (in_array($order[&#39;type&#39;], [&#39;iOS&#39;, &#39;Android&#39;, &#39;Wap&#39;])) {

            $params[&#39;scene_info&#39;][&#39;h5_info&#39;] = [&#39;type&#39; => $order[&#39;type&#39;]];

            $url = self::$transactionsUrl . &#39;h5&#39;; // 拼接请求地址

        } else {

            $url = self::$transactionsUrl . strtolower($order[&#39;type&#39;]); // 拼接请求地址

        }

 

        $header = self::createAuthorization($url, $params, &#39;POST&#39;);

        $response = Http::post($url, json_encode($params, JSON_UNESCAPED_UNICODE), $header);

        $result = json_decode($response, true);

        if (isset($result[&#39;code&#39;]) && isset($result[&#39;message&#39;])) {

            throw new \Exception("[" . $result[&#39;code&#39;] . "] " . $result[&#39;message&#39;]);

        }

 

        return $result;

    }

 

    /**

     * [query 查询订单]

     * @param  [type]  $orderSn [订单编号]

     * @param  boolean $type    [微信支付订单编号,是否是微信支付订单号]

     * @return [type]           [description]

     */

    public static function query($orderSn, $type = false)

    {

        $config = self::$config;

        $url = self::$transactionsUrl . ($type ? &#39;id/&#39; : &#39;out-trade-no/&#39;) . $orderSn . &#39;?mchid=&#39; . $config[&#39;mchid&#39;];

        $params = &#39;&#39;;

 

        $header = self::createAuthorization($url, $params, &#39;GET&#39;);

        $response = Http::get($url, $params, $header);

        $result = json_decode($response, true);

 

        return $result;

    }

 

    /**

     * [close 关闭订单]

     * @param  [type] $orderSn [微信支付订单编号]

     * @return [type]          [description]

     */

    public static function close($orderSn)

    {

        $config = self::$config;

        $url = self::$transactionsUrl . &#39;out-trade-no/&#39; . $orderSn . &#39;/close&#39;;

        $params[&#39;mchid&#39;] = $config[&#39;mchid&#39;];

 

        $header = self::createAuthorization($url, $params, &#39;POST&#39;);

        $response = Http::post($url, json_encode($params, JSON_UNESCAPED_UNICODE), $header);

        $result = json_decode($response, true);

 

        return true;

    }

 

    /**

     * [js 获取jssdk需要用到的数据]

     * @param  [type] $order [订单信息数组]

     * @return [type]        [description]

     */

    public static function js($order=[]){

        $config = self::$config;

        if (!is_array($order) || count($order) < 3)

            die("订单数组信息缺失!");

        if (count($order) == 4 && !empty($order[&#39;openid&#39;])) {

            $data = self::xcx($order, false, false); // 获取支付相关信息(获取非小程序信息)

            return $data;

        }

        $code = !empty($order[&#39;code&#39;]) ? $order[&#39;code&#39;] : ($_GET[&#39;code&#39;] ?? &#39;&#39;);

        $redirectUri = $_SERVER[&#39;REQUEST_SCHEME&#39;] . &#39;://&#39; . $_SERVER[&#39;HTTP_HOST&#39;] . rtrim($_SERVER[&#39;REQUEST_URI&#39;], &#39;/&#39;) . &#39;/&#39;; // 重定向地址

 

        $params = [&#39;appid&#39; => $config[&#39;appid&#39;]];

        // 如果没有get参数没有code;则重定向去获取code;

        if (empty($code)) {

            $params[&#39;redirect_uri&#39;] = $redirectUri; // 返回的url

            $params[&#39;response_type&#39;] = &#39;code&#39;;

            $params[&#39;scope&#39;] = &#39;snsapi_base&#39;;

            $params[&#39;state&#39;] = $order[&#39;order_sn&#39;]; // 获取订单号

 

            $url = self::$authorizeUrl . &#39;?&#39;. http_build_query($params) .&#39;#wechat_redirect&#39;;

        } else {

            $params[&#39;secret&#39;] = $config[&#39;appsecret&#39;];

            $params[&#39;code&#39;] = $code;

            $params[&#39;grant_type&#39;] = &#39;authorization_code&#39;;

 

            $response = Http::get(self::$accessTokenUrl, $params); // 进行GET请求

            $result = json_decode($response, true);

 

            $order[&#39;openid&#39;] = $result[&#39;openid&#39;]; // 获取到的openid

            $data = self::xcx($order, false, false); // 获取支付相关信息(获取非小程序信息)

 

            if (!empty($order[&#39;code&#39;])) {

                return $data;

            }

 

            $url = $config[&#39;redirect_url&#39;] ?? $redirectUri;

            $url .= &#39;?data=&#39; . json_encode($data, JSON_UNESCAPED_UNICODE);

        }

        header(&#39;Location: &#39;. $url);

        die;

    }

 

    /**

     * [app 获取APP支付需要用到的数据]

     * @param  [type]  $order [订单信息数组]

     * @return [type]         [description]

     */

    public static function app($order=[], $log=false)

    {

        if(empty($order[&#39;order_sn&#39;]) || empty($order[&#39;total_amount&#39;]) || empty($order[&#39;body&#39;])){

            die("订单数组信息缺失!");

        }

 

        $order[&#39;type&#39;] = &#39;app&#39;; // 获取订单类型,用户拼接请求地址

        $result = self::unifiedOrder($order, true);

        if (!empty($result[&#39;prepay_id&#39;])) {

            $data = array (

                &#39;appId&#39;     => self::$config[&#39;appid&#39;], // 微信开放平台审核通过的移动应用appid

                &#39;timeStamp&#39; => (string)time(),

                &#39;nonceStr&#39;  => self::get_rand_str(32, 0, 1), // 随机32位字符串

                &#39;prepayid&#39;  => $result[&#39;prepay_id&#39;],

            );

            $data[&#39;paySign&#39;] = self::makeSign($data);

            $data[&#39;partnerid&#39;] = $config[&#39;mchid&#39;];

            $data[&#39;package&#39;] = &#39;Sign=WXPay&#39;;

            return $data; // 数据小程序客户端

        } else {

            return $log ? $result : false;

        }

    }

 

    /**

     * [h5 微信H5支付]

     * @param  [type] $order [订单信息数组]

     * @return [type]        [description]

     */

    public static function h5($order=[], $log=false)

    {

        if(empty($order[&#39;order_sn&#39;]) || empty($order[&#39;total_amount&#39;]) || empty($order[&#39;body&#39;]) || empty($order[&#39;type&#39;]) || !in_array(strtolower($order[&#39;type&#39;]), [&#39;ios&#39;, &#39;android&#39;, &#39;wap&#39;])){

            die("订单数组信息缺失!");

        }

        $result = self::unifiedOrder($order);

        if (!empty($result[&#39;h5_url&#39;])) {

            return $result[&#39;h5_url&#39;]; // 返回链接让用户点击跳转

        } else {

            return $log ? $result : false;

        }

    }

 

    /**

     * [xcx 获取jssdk需要用到的数据]

     * @param  [type]  $order [订单信息数组]

     * @param  boolean $log   [description]

     * @param  boolean $type  [区分是否是小程序,默认 true]

     * @return [type]         [description]

     */

    public static function xcx($order=[], $log=false, $type=true)

    {

        if(empty($order[&#39;order_sn&#39;]) || empty($order[&#39;total_amount&#39;]) || empty($order[&#39;body&#39;]) || empty($order[&#39;openid&#39;])){

            die("订单数组信息缺失!");

        }

 

        $order[&#39;type&#39;] = &#39;jsapi&#39;; // 获取订单类型,用户拼接请求地址

        $config = self::$config;

        $result = self::unifiedOrder($order, $type);

        if (!empty($result[&#39;prepay_id&#39;])) {

            $data = array (

                &#39;appId&#39;     => $type ? $config[&#39;xcxid&#39;] : $config[&#39;appid&#39;], // 由微信生成的应用ID

                &#39;timeStamp&#39; => (string)time(),

                &#39;nonceStr&#39;  => self::get_rand_str(32, 0, 1), // 随机32位字符串

                &#39;package&#39;   => &#39;prepay_id=&#39;.$result[&#39;prepay_id&#39;],

            );

            $data[&#39;paySign&#39;] = self::makeSign($data);

            $data[&#39;signType&#39;] = &#39;RSA&#39;;

            return $data; // 数据小程序客户端

        } else {

            return $log ? $result : false;

        }

    }

 

    /**

     * [scan 微信扫码支付]

     * @param  [type] $order [订单信息数组]

     * @return [type]        [description]

     */

    public static function scan($order=[], $log=false)

    {

        if(empty($order[&#39;order_sn&#39;]) || empty($order[&#39;total_amount&#39;]) || empty($order[&#39;body&#39;])){

            die("订单数组信息缺失!");

        }

        $order[&#39;type&#39;] = &#39;native&#39;; // Native支付

        $result = self::unifiedOrder($order);

 

        if (!empty($result[&#39;code_url&#39;])) {

            return urldecode($result[&#39;code_url&#39;]); // 返回链接让用户点击跳转

        } else {

            return $log ? $result : false;

        }

    }

 

    /**

     * [notify 回调验证]

     * @return [array] [返回数组格式的notify数据]

     */

    public static function notify($server = [], $response = [])

    {

        $config = self::$config;

        $server = $server ?? $_SERVER;

        $response = $response ?? file_get_contents(&#39;php://input&#39;, &#39;r&#39;);

        if (empty($response) || empty($server[&#39;HTTP_WECHATPAY_SIGNATURE&#39;])) {

            return false;

        }

        $body = [

            &#39;timestamp&#39; => $server[&#39;HTTP_WECHATPAY_TIMESTAMP&#39;],

            &#39;nonce&#39; => $server[&#39;HTTP_WECHATPAY_NONCE&#39;],

            &#39;data&#39; => $response,

        ];

        // 验证应答签名

        $verifySign = self::verifySign($body, trim($server[&#39;HTTP_WECHATPAY_SIGNATURE&#39;]), trim($server[&#39;HTTP_WECHATPAY_SERIAL&#39;]));

        if (!$verifySign) {

            die("签名验证失败!");

        }

        $result = json_decode($response, true);

        if (empty($result) || $result[&#39;event_type&#39;] != &#39;TRANSACTION.SUCCESS&#39; || $result[&#39;summary&#39;] != &#39;支付成功&#39;) {

            return false;

        }

        // 加密信息

        $associatedData = $result[&#39;resource&#39;][&#39;associated_data&#39;];

        $nonceStr = $result[&#39;resource&#39;][&#39;nonce&#39;];

        $ciphertext = $result[&#39;resource&#39;][&#39;ciphertext&#39;];

        $data = $result[&#39;resource&#39;][&#39;ciphertext&#39;] = self::decryptToString($associatedData, $nonceStr, $ciphertext);

 

        return json_decode($data, true);

    }

 

    /**

     * [refund 微信支付退款]

     * @param  [type] $order [订单信息]

     * @param  [type] $type  [是否是小程序]

     */

    public static function refund($order)

    {

        $config = self::$config;

        if(empty($order[&#39;refund_sn&#39;]) || empty($order[&#39;refund_amount&#39;]) || (empty($order[&#39;order_sn&#39;]) && empty($order[&#39;transaction_id&#39;]))){

            die("订单数组信息缺失!");

        }

 

        $params = array(

            &#39;out_refund_no&#39; => (string)$order[&#39;refund_sn&#39;], // 商户退款单号

            &#39;funds_account&#39; => &#39;AVAILABLE&#39;, // 退款资金来源

            &#39;amount&#39; => [

                    &#39;refund&#39; => $order[&#39;refund_amount&#39;],

                    &#39;currency&#39; => &#39;CNY&#39;,

                ]

        );

 

        if (!empty($order[&#39;transaction_id&#39;])) {

            $params[&#39;transaction_id&#39;] = $order[&#39;transaction_id&#39;];

            $orderDetail = self::query($order[&#39;transaction_id&#39;], true);

        } else {

            $params[&#39;out_trade_no&#39;] = $order[&#39;order_sn&#39;];

            $orderDetail = self::query($order[&#39;order_sn&#39;]);

        }

        $params[&#39;amount&#39;][&#39;total&#39;] = $orderDetail[&#39;amount&#39;][&#39;total&#39;];

        !empty($order[&#39;reason&#39;]) && $params[&#39;reason&#39;] = $order[&#39;reason&#39;];

 

        $url = self::$refundUrl;

        $header = self::createAuthorization($url, $params, &#39;POST&#39;);

        $response = Http::post($url, json_encode($params, JSON_UNESCAPED_UNICODE), $header);

        $result = json_decode($response, true);

 

        return $result;

    }

 

    /**

     * [queryRefund 查询退款]

     * @param  [type] $refundSn [退款单号]

     * @return [type]           [description]

     */

    public static function queryRefund($refundSn, $type = false)

    {

        $url = self::$refundUrl . &#39;/&#39; . $refundSn;

        $params = &#39;&#39;;

 

        $header = self::createAuthorization($url, $params, &#39;GET&#39;);

        $response = Http::get($url, $params, $header);

        $result = json_decode($response, true);

 

        return $result;

    }

 

    /**

     * [success 通知支付状态]

     */

    public static function success()

    {

        $str = [&#39;code&#39;=>&#39;SUCCESS&#39;, &#39;message&#39;=>&#39;成功&#39;];

        die(json_encode($str, JSON_UNESCAPED_UNICODE));

    }

 

    /**

     * [createAuthorization 获取接口授权header头信息]

     * @param  [type] $url    [请求地址]

     * @param  array  $data   [请求参数]

     * @param  string $method [请求方式]

     * @return [type]         [description]

     */

    //生成v3 Authorization

    protected static function createAuthorization($url, $data=[], $method=&#39;POST&#39;){

        $config = self::$config;

        //商户号

        $mchid = $config[&#39;mchid&#39;];

        // 证书序列号

        if (empty($config[&#39;serial_no&#39;])) {

            $certFile = @file_get_contents($config[&#39;cert_client&#39;]);

            $certArr = openssl_x509_parse($publicStr);

            $serial_no = $certArr[&#39;serialNumberHex&#39;];

        } else {

            $serial_no = $config[&#39;serial_no&#39;];

        }

 

        // 解析url地址

        $url_parts = parse_url($url);

        //生成签名

        $body = [

            &#39;method&#39; => $method,

            &#39;url&#39;   => ($url_parts[&#39;path&#39;] . (!empty($url_parts[&#39;query&#39;]) ? "?${url_parts[&#39;query&#39;]}" : "")),

            &#39;time&#39;  => time(), // 当前时间戳

            &#39;nonce&#39; => self::get_rand_str(32, 0, 1), // 随机32位字符串

            &#39;data&#39;  => (strtolower($method) == &#39;post&#39; ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data), // POST请求时 需要 转JSON字符串

        ];

        $sign = self::makeSign($body);

        //Authorization 类型

        $schema = &#39;WECHATPAY2-SHA256-RSA2048&#39;;

        //生成token

        $token = sprintf(&#39;mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"&#39;, $mchid, $body[&#39;nonce&#39;], $body[&#39;time&#39;], $serial_no, $sign);

 

        $header = [

            &#39;Content-Type:application/json&#39;,

            &#39;Accept:application/json&#39;,

            &#39;User-Agent:*/*&#39;,

            &#39;Authorization: &#39;.  $schema . &#39; &#39; . $token

        ];

        return $header;

    }

 

    /**

     * [makeSign 生成签名]

     * @param  [type] $data [加密数据]

     * @return [type]       [description]

     */

    public static function makeSign($data)

    {

        $config = self::$config;

        if (!in_array(&#39;sha256WithRSAEncryption&#39;, \openssl_get_md_methods(true))) {

            throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");

        }

        // 拼接生成签名所需的字符串

        $message = &#39;&#39;;

        foreach ($data as $value) {

            $message .= $value . "\n";

        }

        // 商户私钥

        $private_key = self::getPrivateKey($config[&#39;cert_key&#39;]);

        // 生成签名

        openssl_sign($message, $sign, $private_key, &#39;sha256WithRSAEncryption&#39;);

        $sign = base64_encode($sign);

        return $sign;

    }

 

    /**

     * [verifySign 验证签名]

     * @param  [type] $data   [description]

     * @param  [type] $sign   [description]

     * @param  [type] $serial [description]

     * @return [type]         [description]

     */

    public static function verifySign($data, $sign, $serial)

    {

        $config = self::$config;

        if (!in_array(&#39;sha256WithRSAEncryption&#39;, \openssl_get_md_methods(true))) {

            throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");

        }

        $sign = \base64_decode($sign);

        // 拼接生成签名所需的字符串

        $message = &#39;&#39;;

        foreach ($data as $value) {

            $message .= $value . "\n";

        }

        // 获取证书相关信息

        self::certificates($serial);

        // 平台公钥

        $public_key = self::getPublicKey($config[&#39;public_key&#39;]); //平台公钥

        // 验证签名

        $recode = \openssl_verify($message, $sign, $public_key, &#39;sha256WithRSAEncryption&#39;);

        return $recode == 1 ? true : false;

    }

 

    //获取私钥

    public static function getPrivateKey($filepath)

    {

        return openssl_pkey_get_private(file_get_contents($filepath));

    }

 

    //获取公钥

    public static function getPublicKey($filepath)

    {

        return openssl_pkey_get_public(file_get_contents($filepath));

    }

 

    /**

     * [certificates 获取证书]

     * @return [type] [description]

     */

    public static function certificates($serial)

    {

        $config = self::$config;

 

        $publicStr = @file_get_contents($config[&#39;public_key&#39;]);

        if ($publicStr) { // 判断证书是否存在

            $openssl = openssl_x509_parse($publicStr);

            if ($openssl[&#39;serialNumberHex&#39;] == $serial) { // 是否是所需证书

                // return self::getPublicKey($config[&#39;public_key&#39;]); //平台公钥

                return &#39;&#39;;

            }

        }

 

        $url = self::$certificatesUrl;

        $params = &#39;&#39;;

 

        $header = self::createAuthorization($url, $params, &#39;GET&#39;);

        $response = Http::get($url, $params, $header);

        $result = json_decode($response, true);

        if (empty($result[&#39;data&#39;])) {

            throw new RuntimeException("[" . $result[&#39;code&#39;] . "] " . $result[&#39;message&#39;]);

        }

        foreach ($result[&#39;data&#39;] as $key => $certificate) {

            if ($certificate[&#39;serial_no&#39;] == $serial) {

                $publicKey = self::decryptToString(

                    $certificate[&#39;encrypt_certificate&#39;][&#39;associated_data&#39;],

                    $certificate[&#39;encrypt_certificate&#39;][&#39;nonce&#39;],

                    $certificate[&#39;encrypt_certificate&#39;][&#39;ciphertext&#39;]

                );

                file_put_contents($config[&#39;public_key&#39;], $publicKey);

                break; // 终止循环

            }

            // self::$publicKey[$certificate[&#39;serial_no&#39;]] = $publicKey;

        }

        // return self::getPublicKey($config[&#39;public_key&#39;]); //平台公钥

    }

 

    /**

     * [decryptToString 证书和回调报文解密]

     * @param  [type] $associatedData [附加数据包(可能为空)]

     * @param  [type] $nonceStr       [加密使用的随机串初始化向量]

     * @param  [type] $ciphertext     [Base64编码后的密文]

     * @return [type]                 [description]

     */

    public static function decryptToString($associatedData, $nonceStr, $ciphertext)

    {

        $config = self::$config;

        $ciphertext = base64_decode($ciphertext);

        if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {

            return false;

        }

 

        // ext-sodium (default installed on >= PHP 7.2)

        if (function_exists(&#39;\sodium_crypto_aead_aes256gcm_is_available&#39;) &&

            \sodium_crypto_aead_aes256gcm_is_available()) {

            return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $config[&#39;key&#39;]);

        }

 

        // ext-libsodium (need install libsodium-php 1.x via pecl)

        if (function_exists(&#39;\Sodium\crypto_aead_aes256gcm_is_available&#39;) &&

            \Sodium\crypto_aead_aes256gcm_is_available()) {

            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $config[&#39;key&#39;]);

        }

 

        // openssl (PHP >= 7.1 support AEAD)

        if (PHP_VERSION_ID >= 70100 && in_array(&#39;aes-256-gcm&#39;, \openssl_get_cipher_methods())) {

            $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);

            $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);

 

            return \openssl_decrypt($ctext, &#39;aes-256-gcm&#39;, $config[&#39;key&#39;], \OPENSSL_RAW_DATA, $nonceStr,

                $authTag, $associatedData);

        }

 

        throw new \RuntimeException(&#39;AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php&#39;);

    }

 

    /** fengkui.net

     * [get_rand_str 获取随机字符串]

     * @param  integer $randLength    [长度]

     * @param  integer $addtime       [是否加入当前时间戳]

     * @param  integer $includenumber [是否包含数字]

     * @return [type]                 [description]

     */

    public static function get_rand_str($randLength=6, $addtime=0, $includenumber=1)

    {

        if ($includenumber)

            $chars=&#39;abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789&#39;;

        $chars=&#39;abcdefghijklmnopqrstuvwxyz&#39;;

 

        $len = strlen($chars);

        $randStr = &#39;&#39;;

        for ($i=0; $i<$randLength; $i++){

            $randStr .= $chars[rand(0, $len-1)];

        }

        $tokenvalue = $randStr;

        $addtime && $tokenvalue = $randStr . time();

        return $tokenvalue;

    }

 

    /** fengkui.net

     * [get_ip 定义一个函数get_ip() 客户端IP]

     * @return [type] [description]

     */

    public static function get_ip()

    {

        if (getenv("HTTP_CLIENT_IP"))

            $ip = getenv("HTTP_CLIENT_IP");

        else if(getenv("HTTP_X_FORWARDED_FOR"))

            $ip = getenv("HTTP_X_FORWARDED_FOR");

        else if(getenv("REMOTE_ADDR"))

            $ip = getenv("REMOTE_ADDR");

        else $ip = "Unknow";

 

        if(preg_match(&#39;/^((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1 -9]?\d))))$/&#39;, $ip))

            return $ip;

        else

            return &#39;&#39;;

    }

}

로그인 후 복사

이 기사에 대한 참조 문서:
1, WeChat 결제 애플릿(v3) - PHP 전체 백엔드 코드
2, PHP WeChat Mini 프로그램 WeChat Pay v3
3, WeChat Pay V3 버전 미니 프로그램 결제 php 서명, 서명 확인, 데이터 복호화 코드 공유(전체 메소드 마스터 참조)
4, WeChat Payment API v3 콜백 알림 서명 확인 PHPdemo가 있나요?

추천: "PHP 비디오 튜토리얼"

위 내용은 WeChat 결제가 업그레이드되면서 PHP WeChat 결제 V3 인터페이스도 출시되었습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
php
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿