字节跳动小程序支付详解

本文档详细介绍了在字节跳动小程序中集成微信支付的两种实现方式,包括获取预支付单号、生成支付宝APP支付的orderInfo以及创建字节跳动小程序支付请求参数的步骤。作者指出官方文档描述过于简单,通过自己的实践整理出了清晰的实现过程。
摘要由CSDN通过智能技术生成

微信支付详解

1.为什么要写这篇文章

参考了字节跳动官方的文档之后发现写的太简单,完全一头雾水摸不清头脑,后来在百度了别人的实现方案,才得以总结出来。

2.背景

我司要开发一个头条小程序,需要支持支付宝和微信支付

3.签约以及配置流程(不过多描述)

4.业务代码

一开始看了字节跳动的官方文档tt.pay确实是没怎么看懂(可能是能力问题),而且demo中也没有php的案例,让phper实在是为难,后来百度了一下,发现广大的网友还是很强大的,总结了一下实现的方式:

第一种

  • 第一步:获取预支付单号
  • 第二步:获取支付宝APP支付返回的orderInfo
  • 第三步:生成字节跳动小程序支付请求参数
    但是这种我在官方文档中并没有发现这中实现方式,可能是文档更新了(疑问疑问??)
<?php
/**
 * Created by PhpStorm.
 * User: langziqiang
 * Date: 2020-05-22
 * Time: 10:43
 */

namespace Common\Service;

use http\Exception;

class TouTiaoService{

    private $tt_config;         //头条小程序配置
    private $order;             //订单信息
    private $user;              //用户信息
    private $ip;                //用户ip,调用预支付单号需要传
    private $tt_trade_no;       //支付预支付单号
    private $ali_orderinfo;     //支付宝app支付orderInfo

    const PRE_PAY_NUMBER_URL = 'https://tp-pay.snssdk.com/gateway';

    public function __construct($order, $uid, $ip){
        $this->tt_config = C('ZIJIETIAODONG');
        $this->order     = $order;
        $this->user      = M('Users')->find($uid);
        $this->ip        = $ip;
    }
    /**
     * Notes:入口
     * User: langziqiang
     * Date: 2020-05-22
     * Time: 14:28
     * @return array
     */
    public function getTouTiaoAliPayResult(){
        //第一步:获取预支付单号
        $this->createPrePayNumber();

        //第二步:获取支付宝APP支付返回的orderInfo
        $this->createAliAppPay();

        //第三步:生成字节跳动小程序支付请求参数
        $res = $this->createProgramData();
        return $res;
    }

    /**
     * Notes:获取预支付单号
     * User: langziqiang
     * Date: 2020-05-22
     * Time: 11:04
     */
    public function createPrePayNumber(){
        //业务数据
        $biz_content = array(
            'out_order_no' => date("YmdHi") . intval(microtime() * 1000) . $this->user['uid'],   //商户唯一订单号
            'uid'          => 'openid',                                                                     //头条用户openid
            'merchant_id'  => $this->tt_config['merchant_id'],                                              //字节跳动小程序支付中的商户id
            'total_amount' => $this->order['all_money'] * 100,                                              //订单金额
            'currency'     => 'CNY',                                                                        //固定CNY
            'subject'      => $this->user['company'] . "的账单",                                             //商户订单名称
            'body'         => $this->user['company'] . "的账单",                                             //商户订单详情
            'trade_time'   => time(),                                                                       //下单时间戳,精确到秒
            'valid_time'   => '120',                                                                        //订单有效时间(单位 秒)
            'notify_url'   => get_ali_app_notify(),                                                         //填任意非空 URL 即可
            'risk_info'    => json_encode(['ip' => $this->ip])                                              // 输出示例: "{\"ip\":\"110.90.28.70\"}",
        );

        $biz_content = json_encode($biz_content);
        //公共请求参数
        $params['app_id']      = $this->tt_config['app_id'];        //头条appid
        $params['biz_content'] = $biz_content;                      //业务数据json
        $params['method']      = 'tp.trade.create';                 //注意:此处固定值 tp.trade.create
        $params['charset']     = 'utf-8';                           //定值
        $params['sign_type']   = 'MD5';                             //定值
        $params['timestamp']   = time();                            //当前时间戳
        $params['version']     = 2.0;                               //定值


        $this->getSignContent($params);
        //注意post的参数必须是a=1&b=2这种格式,官方文档并没有说明
        $res = Http::curl_post2(self::PRE_PAY_NUMBER_URL, http_build_query($params));
        $res = json_decode($res, true);
        $res = $res['response'];
        if($res['code'] == '10000'){
            $this->tt_trade_no = $res['trade_no']; //头条预支付单号
        }else{
            E("支付失败!");
        }
    }

    /**
     * Notes:创建支付宝app支付
     * User: langziqiang
     * Date: 2020-05-22
     * Time: 14:23
     */
    public function createAliAppPay(){
        $alipay              = new AliPayService();
        $alipay->order_id    = $this->order['order_id'];
        $alipay->order       = $this->order;
        $orderInfo           = $alipay->aliAppPay($this->order);
        $this->ali_orderinfo = $orderInfo;
    }


    /**
     * Notes:组装小程序唤起参数
     * User: langziqiang
     * Date: 2020-05-22
     * Time: 14:23
     * @return array
     */
    public function createProgramData(){
        $res                 = [];
        $res['app_id']       = $this->tt_config['app_id'];                      //字节跳动小程序app_id
        $res['sign_type']    = 'MD5';                                           //定值
        $res['uid']          = 'open_id';                                       //字节跳动小程序open_id
        $res['total_amount'] = $this->order['all_money'] * 100;                 //金额
        $res['timestamp']    = (string)time();                                  //当前时间戳
        $res['trade_no']     = $this->tt_trade_no;                              //注意:这里为字节跳动小程序返回的预支付单号
        $res['merchant_id']  = $this->tt_config['merchant_id'];                 //字节跳动商户号
        $res['params']       = json_encode(['url' => $this->ali_orderinfo]);    //特殊parme,json
        $this->getSignContent($res);                                   //sign
        $res['method']      = 'tp.trade.confirm';                               //注意:这里于获取预支付单号不同,固定为 tp.trade.confirm
        $res['pay_channel'] = 'ALIPAY_NO_SIGN';                                 //定值
        $res['pay_type']    = "ALIPAY_APP";                                     //定值
        $res['risk_info']   = $this->ip;                                        //ip
        $res['url']         = $this->ali_orderinfo;                             //支付宝链接
        return $res;
    }

    /**
     * 签名处理
     * @param $params
     * @param $charset
     * @return string
     */
    public function getSignContent(&$params, $charset = 'utf-8'){
        ksort($params);
        $stringToBeSigned = "";
        $i                = 0;
        foreach($params as $k => $v){
            if(false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)){
                // 转换成目标字符集
                $v = $this->characet($v, $charset);
                if($i == 0){
                    $stringToBeSigned .= "$k" . "=" . "$v";
                }else{
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        $stringToBeSigned = $stringToBeSigned . $this->tt_config['secret'];
        unset ($k, $v);
        $params['sign'] = md5($stringToBeSigned);
    }


    /**
     * 校验$value是否非空
     * @param $value
     * @return  boolean;
     *  if not set ,return true;
     *  if is null , return true;
     **/
    public function checkEmpty($value){
        if(!isset($value))
            return true;
        if($value === null)
            return true;
        if(trim($value) === "")
            return true;
        return false;
    }


    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    public function characet($data, $targetCharset){
        if(!empty($data)){
            $fileType = "UTF-8";
            if(strcasecmp($fileType, $targetCharset) != 0){
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
            }
        }
        return $data;
    }
}

第二种

  • 直接返回orderInfo给前端
 public function getTouTiaoAliPayResult2(){
        $data               = [
            'merchant_id'  => $this->tt_config['merchant_id'],                                                //头条商户id
            'app_id'       => $this->tt_config['app_id'],                                                     //头条appId
            'sign_type'    => 'MD5',                                                                          //定值
            'out_order_no' => date("YmdHi") . intval(microtime() * 1000) . $this->user['uid'],     //商家订单号
            'timestamp'    => time(),                                                                          //时间戳
            'version'      => '2.0',                                                                           //定值
            'trade_type'   => 'H5',                                                                             //定值
            'product_code' => 'pay',                                                                            //定值
            'payment_type' => 'direct',                                                                         //定值
            'uid'          => 'open_id',                                                                        //头条openid
            'total_amount' => intval($this->order['all_money'] * 100),                                      //订单金额
            'currency'     => 'CNY',                                                                            //定值
            'subject'      => $this->user['company'] . "的账单",                                                 //商品名称
            'body'         => $this->user['company'] . "的账单",                                                 //商品详情
            'trade_time'   => time(),                                                                           //下单时间戳
            'valid_time'   => 120,                                                                              //订单有效时间(单位 秒)
            'notify_url'   => get_ali_app_notify(),                                                             //回调地址
            'risk_info'    => json_encode(['ip' => $this->ip]),                                                 //用户ip
        ];
        $data['alipay_url'] = $this->createAliAppPay($data);                                                    //支付宝url
        $this->getSignContent($data, 'utf-8');                                                  //签名
        dump($data);
    }

小程序前段代码(针对第二种实现方案)

$.ajax({
            url: "后台接口",
            method: "POST",
            data: {
              cid: that.data.info.id
            },
            success(r) {
              tt.pay({
                orderInfo: r.data,
                service: 1,
                getOrderStatus(res) {
                  let { out_order_no } = res;
                  return new Promise(function (resolve, reject) {
                    // 商户前端根据 out_order_no 请求商户后端查询微信支付订单状态
                    tt.request({
                      url: "https://www.xxxxx.com/api/v1/wechat/notify",
                      data: { out_order_no: out_order_no },
                      method: 'POST',
                      success(res) {
                        // 商户后端查询的微信支付状态,通知收银台支付结果
                        resolve({ code: res.data.code })
                      },
                      fail(err) {
                        reject(err);
                      }
                    });
                  });
                },
                success(res) {
                  if (res.code == 0) {
                    // 支付成功处理逻辑,只有res.code=0时,才表示支付成功
                    // 但是最终状态要以商户后端结果为准
                    that.onLoad(that.data.info)
                  }
                },
                fail(res) {
                  // 调起收银台失败处理逻辑
                  tt.showModal({
                    content: JSON.stringify(res) + '1'
                  });
                }
              });
              console.log(r.data)
            }
          })

总结:字节跳动的api文档写的真的不可描述

大家有问题可以相互交流

一起来学习Golang吧

golang

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值