抖音小程序开发 唤起收银台支付(可以选择支付宝APP支付或微信H5支付)

简介: 字节跳动也开放了小程序给商家接入,可以在旗下APP如抖音、今日头条、今日头条极速版等应用中即点即用,基于庞大的数亿用户量为其引流,帮助商家获取用户流量,销售商品,其模式和微信小程序差不多。

听我说哈,先看效果:

image.png

字节跳动也开放了小程序给商家接入,可以在旗下APP如抖音、今日头条、今日头条极速版等应用中即点即用,基于庞大的数亿用户量为其引流,帮助商家获取用户流量,销售商品,其模式和微信小程序差不多。

1、后台管理地址:

https://developer.toutiao.com/

image.png

2、开发文档地址:

https://developer.toutiao.com/dev/cn/mini-app/develop/open-capacity/log-in/tt.login

image.png

不像微信和支付宝,字节跳动没有自己的支付,但是在小程序里集成了微信和支付宝支付可供商家满足支付的需求,接下来来看代码是怎么实现的。

首先要在小程序平台上开通支付功能,这个直接去看开发文档,里面的教程说得很清楚,这里就不再敖述。特别要注意的是,申请完微信支付,要登陆微信商户号对应的商户平台 - "产品中心" - "开发配置"自行配置H5 支付域名:snssdk.com,不然微信支付会报错。

再则字节跳动小程序集成微信和支付宝支付,前提要先调通单纯的微信H5支付和支付宝支付。

代码实现

实体类:TTPayParamIn

 * @description
 */
@Data
public class TTPayParamIn {

    /**
     * 头条小程序分配给商户的商户id
     */
    private String toutiaoMchId;
    /**
     * 头条小程序分配给商户的appid
     */
    private String toutiaoMchAppId;
    /**
     * 头条小程序分配给商户的appSecrect
     */
    private String toutiaoMchSecrect;
    /**
     * 唯一标识用户的id,小程序开发者请传open_id
     */
    private String uid;
    /**
     * 金额,单位:元
     */
    private Long totalAmount;
    /**
     * 商户订单名称
     */
    private String subject;
    /**
     * 商户订单详情
     */
    private String body;
    /**
     * 订单有效时间,单位:秒
     */
    private String validTime;
    /**
     * 服务器异步通知http地址,请填支付宝下单接口对应的异步通知url
     */
    private String notifyUrl;
    /**
     * 订单号
     */
    private String outOrderNo;
}

实体类:CheckoutCounterIn

 * @description
 */
@Data
public class CheckoutCounterIn extends  TTPayParamIn {

    //支付宝支付信息
    /**
     * 支付宝应用id
     */
    private String alipayAppId;
    /**
     * 支付宝应用私钥
     */
    private String alipayAppPrivateKey;
    /**
     * 支付宝公钥
     */
    private String alipayPublicKey;
    /**
     * 支付宝公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。
     */
    private String passbackParams;
    /**
     * 支付宝回调地址
     */
    private String alipayNotifyUrl;

    //微信支付信息
    /**
     * 微信小程序appid
     */
    private String wxAppId;
    /**
     * 微信小程序商户id
     */
    private String wxMchId;
    /**
     * 微信小程序商户支付apikey
     */
    private String wxApiKey;
    /**
     * 微信小程序支付回调地址
     */
    private String wxNotifyUrl;
    /**
     * 服务器地址 如:https://xxx.xxx.com/xxx
     */
    private String serverPath;
    /**
     * 金额
     */
    private Double wxMoney;
    /**
     * 微信自定义参数
     */
    private String wxAttach;
}

支付通用工具类:PayCommonUtil

 * @description
 */
public class PayCommonUtil {
  private static Logger log = LoggerFactory.getLogger(PayCommonUtil.class);
  
  public static String CreateNoncestr() {
    String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    String res = "";
    for (int i = 0; i < 16; i++) {
      Random rd = new Random();
      res += chars.charAt(rd.nextInt(chars.length() - 1));
    }
    return res;
  }
  
  /**
   * 
   * @Title: createSign
   * @Description:sign签名
   * @param characterEncoding 编码格式
   * @param parameters 请求参数
   * @param @return 设定文件
   * @return String 返回类型
   * @throws
   */
  public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters,String apiKey){
    StringBuffer sb = new StringBuffer();
    Set es = parameters.entrySet();
    Iterator it = es.iterator();
    while(it.hasNext()) {
      Map.Entry entry = (Map.Entry)it.next();
      String k = (String)entry.getKey();
      Object v = entry.getValue();
      if(null != v && !"".equals(v) 
          && !"sign".equals(k) && !"key".equals(k)) {
        sb.append(k + "=" + v + "&");
      }
    }
    sb.append("key=" + apiKey);
    String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    
    log.info("createSign方法sign签名=====:"+sign);
    return sign;
  }
  /**
   * 
   * @Title: getRequestXml
   * @Description:将请求参数转换为xml格式的string
   * @param parameters 请求参数
   * @return String 返回类型
   * @throws
   */
  public static String getRequestXml(SortedMap<Object,Object> parameters){
    StringBuffer sb = new StringBuffer();
    sb.append("<xml>");
    Set es = parameters.entrySet();
    Iterator it = es.iterator();
    while(it.hasNext()) {
      Map.Entry entry = (Map.Entry)it.next();
      String k = (String)entry.getKey();
      String v = (String)entry.getValue();
      if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
        sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
      }else {
        sb.append("<"+k+">"+v+"</"+k+">");
      }
    }
    sb.append("</xml>");
    return sb.toString();
  }
  
  /**
   * 
   * @Title: setXML
   * @Description:返回给微信的参数
   * @param return_code 返回编码
   * @param return_msg 返回信息
   * @param @return 设定文件
   * @return String 返回类型
   * @throws
   */
  public static String setXML(String return_code, String return_msg) {
    return "<xml><return_code><![CDATA[" + return_code
        + "]]></return_code><return_msg><![CDATA[" + return_msg
        + "]]></return_msg></xml>";
  }
  
  //对请求参数进行组装+key
  public static String toutiaoAlipaySign(Map<String, Object> map,String salt) {

    String result = getSignCheckContent(map);
    if (StringUtils.isBlank(salt)) {
      return result;
    }
    result=result + salt;

    String sign = DigestUtils.md5Hex(result.toString());
    log.info("toutiaoAlipaySign-content={};sign={}",result,sign);

    return sign;

  }

  //组装参数
  public static String getSignCheckContent(Map<String, Object> map) {

    ArrayList<String> list = new ArrayList<String>();
    for (Map.Entry<String, Object> entry : map.entrySet()) {

      if (entry.getKey().equals("sign")) {
        continue;
      }
      if (entry.getValue() != null && entry.getValue() != "") {
        list.add(entry.getKey() + "=" + entry.getValue() + "&");
      }
    }
    int size = list.size();
    String[] arrayToSort = list.toArray(new String[size]);
    Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < size; i++) {
      sb.append(arrayToSort[i]);
    }
    String result = sb.toString();
    result = result.substring(0, result.length()-1);
    return result;
  }
}

请求工具类:CommonUtil

 * @description
 */
public class CommonUtil {
  private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
  /**
   * @Title: httpsRequest
   * @Description: 发送https请求 ---返回字符串
   * @param requestUrl 请求地址
   * @param requestMethod 请求方式(GET、POST)
   * @param outputStr 提交的数据
   * @return 返回微信服务器响应的信息
   * @throws
   */
  public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
    try {
      // 创建SSLContext对象,并使用我们指定的信任管理器初始化
      TrustManager[] tm = { new MyX509TrustManager() };
      SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
      sslContext.init(null, tm, new java.security.SecureRandom());
      // 从上述SSLContext对象中得到SSLSocketFactory对象
      SSLSocketFactory ssf = sslContext.getSocketFactory();
      URL url = new URL(requestUrl);
      HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
      conn.setSSLSocketFactory(ssf);
      conn.setDoOutput(true);
      conn.setDoInput(true);
      conn.setUseCaches(false);
      // 设置请求方式(GET/POST)
      conn.setRequestMethod(requestMethod);
      conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
    //conn.setRequestProperty("Content-Type", "text/html;charset=utf-8"); 
      // 当outputStr不为null时向输出流写数据
      if (null != outputStr) {
        OutputStream outputStream = conn.getOutputStream();
        // 注意编码格式
        outputStream.write(outputStr.getBytes("UTF-8"));
        outputStream.close();
      }
      // 从输入流读取返回内容
      InputStream inputStream = conn.getInputStream();
      InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
      BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
      String str = null;
      StringBuffer buffer = new StringBuffer();
      while ((str = bufferedReader.readLine()) != null) {
        buffer.append(str);
      }
      // 释放资源
      bufferedReader.close();
      inputStreamReader.close();
      inputStream.close();
      inputStream = null;
      conn.disconnect();
      System.out.println(buffer.toString());
      return buffer.toString();
    } catch (ConnectException ce) {
      log.error("连接超时:{}", ce);
    } catch (Exception e) {
      log.error("https请求异常:{}", e);
    }
    return null;
  }
}

xml 工具类:XMLUtil

/**
 * @description
 */
public class XMLUtil {
  /**
   * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
   * @param strxml
   * @return
   * @throws JDOMException
   * @throws IOException
   */
  public static Map<String,String> doXMLParse(String strxml) throws JDOMException, IOException {
    strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

    if(null == strxml || "".equals(strxml)) {
      return null;
    }
    
    Map<String,String> m = new HashMap<>();
    
    InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
    SAXBuilder builder = new SAXBuilder();
    Document doc = builder.build(in);
    Element root = doc.getRootElement();
    List list = root.getChildren();
    Iterator it = list.iterator();
    while(it.hasNext()) {
      Element e = (Element) it.next();
      String k = e.getName();
      String v = "";
      List children = e.getChildren();
      if(children.isEmpty()) {
        v = e.getTextNormalize();
      } else {
        v = XMLUtil.getChildrenText(children);
      }
      
      m.put(k, v);
    }    
    //关闭流
    in.close();
    
    return m;
  }
  
  /**
   * 获取子结点的xml
   * @param children
   * @return String
   */
  public static String getChildrenText(List children) {
    StringBuffer sb = new StringBuffer();
    if(!children.isEmpty()) {
      Iterator it = children.iterator();
      while(it.hasNext()) {
        Element e = (Element) it.next();
        String name = e.getName();
        String value = e.getTextNormalize();
        List list = e.getChildren();
        sb.append("<" + name + ">");
        if(!list.isEmpty()) {
          sb.append(XMLUtil.getChildrenText(list));
        }
        sb.append(value);
        sb.append("</" + name + ">");
      }
    }
    return sb.toString();
  }
}

微信H5支付,调用统一下单接口返回的 mweb_url 字段值:WeixinPay

 * @description
 */
public class WeixinPay {

    public static Logger logger = LoggerFactory.getLogger(WeixinPay.class);

    /**
     * 微信支付
     * @param wxPayParamVo
     * @param request
     * @return
     */
    public static SortedMap<Object, Object> weixinPayMobile(WxPayParamVo wxPayParamVo, HttpServletRequest request) {
        SortedMap<Object, Object> params = new TreeMap<>();
        try {
            Double moneys = wxPayParamVo.getMoney() * 100;
            // 我们发送给微信服务器的参数是xml格式的string,微信返回来的信息也是xml格式的string
            // 获取package包
            SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
            parameters.put("appid", wxPayParamVo.getAppId());
            parameters.put("mch_id", wxPayParamVo.getMchId());
            parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());
            parameters.put("body", wxPayParamVo.getBody());
            parameters.put("out_trade_no", wxPayParamVo.getOutTradeNo());// 订单号
            parameters.put("total_fee", moneys.longValue() + "");// 订单总金额,单位为分,不能带小数点   moneys.longValue()
            parameters.put("spbill_create_ip", request.getRemoteAddr());// 终端ip
            parameters.put("notify_url", wxPayParamVo.getNotifyUrl());// 支付成功微信端回调我们接收返回信息的控制器地址
            parameters.put("trade_type", wxPayParamVo.getTradeType());// 交易类型:JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付
            parameters.put("attach", wxPayParamVo.getAttach());// 附加数据--支付类型
            String sign = PayCommonUtil.createSign("UTF-8", parameters, wxPayParamVo.getApiKey());// 进行签名
            parameters.put("sign", sign);
            // 将请求的参数转为字符串
            String requestXML = PayCommonUtil.getRequestXml(parameters);
            logger.info("post请求微信支付参数============:" + requestXML);
            // 将这些参数以POST方式调用微信统一支付接口
            String result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);

            logger.info("统一支付接口返回的参数==============:" + result.toString());
            // 解析微信返回的信息,以Map形式存储便于取值
            @SuppressWarnings("unchecked")
            Map<String, String> map = XMLUtil.doXMLParse(result);
            logger.info("return_code===============:" + map.get("return_code") + "--------------result_code:" + map.get("result_code"));
            logger.info("通过微信支付统一下单接口获取prepay_id==============:" + map.get("prepay_id"));
            params.put("prepayId", map.get("prepay_id"));
            params.put("mwebUrl", map.get("mweb_url"));
            return params;
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //============结束==================
        return params;
    }

    /**
     * 微信支付回调
     * @param API_KEY
     * @param request
     * @param response
     * @return
     */
    public static JsonModel weixinPayCallBack(String API_KEY, HttpServletRequest request, HttpServletResponse response) {
        logger.info("***************************微信端订单【商城 小程序】进入回调页面***************************");
        CallBackVo backVo = new CallBackVo();
        try {
            // 创建支付应答对象
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息

            logger.info("微支付回调返回参数:=====" + result);
            // 进行验签操作
            ResponseHandler resHandler = new ResponseHandler(request, null);
            resHandler.doParse(result);
            //resHandler.setKey(WeiXinPayConfig.API_KEY);
            resHandler.setKey(API_KEY);
            // 创建请求对象
            // 财付通签名认证
            if (resHandler.isValidSign() == true) {
                logger.info("===========财付通签名认证成功=====");
                // 商户订单号
                String out_trade_no = resHandler.getParameter("out_trade_no");
                // 金额,以分为单位
                String total_fee = resHandler.getParameter("total_fee");
                // 获取附加数据
                String attach = resHandler.getParameter("attach");
                String[] attachArray = attach.split("&");
                //微信支付订单号
                String transactionId = resHandler.getParameter("transaction_id");
                logger.info("微信支付订单号" + transactionId);
                // 支付结果
                String result_code = resHandler.getParameter("result_code");
                logger.info("********签名返回值状态result_code:" + result_code);
                //objectId是微信预下单时自定义参数,一般传入订单号
                backVo.setObjectId(attachArray[0]);
                //attach是微信预下单时自定义参数,如123&456&789
                backVo.setAttach(attach);
                backVo.setOutTradeNo(out_trade_no);
                backVo.setWeixinPayNo(transactionId);
                // 判断签名及结果
                if (result_code.equalsIgnoreCase("SUCCESS")) {
                    logger.info("===========【商城 小程序】签名SUCCESS即时到账处理业务开始=====");
                    // ------------------------------
                    // 即时到账处理业务开始
                    // ------------------------------
                    String money = AmountUtils.changeF2Y(total_fee);
                    logger.info("===========【商城 小程序】success 后台通知成功=====");
                    ServletOutputStream outputStream = response.getOutputStream();
                    outputStream.write(PayCommonUtil.setXML("SUCCESS", "回调成功=== " + "金额:" + money).getBytes()); // 告诉微信服务器,我收到信息了
                    outputStream.flush();
                  outputStream.close();
                  // 给财付通系统发送成功信息,财付通系统收到此结果后不再进行后续通知
                    logger.info("商户订单号:" + out_trade_no + "微信商城 小程序支付回调成功");
                    return JsonModel.toSuccess(backVo);
                } else {
                    logger.error("微信【商城 小程序】支付失败");
                    return JsonModel.toFail(backVo, "微信支付失败");
                }
            } else {// MD5签名失败
                logger.error("微信【商城 小程序】支付MD5签名失败");
                return JsonModel.toFail(null, "支付MD5签名失败");
            }
        } catch (Exception e) {
            logger.error("微信支付【商城 小程序】异常=====:" + e.getMessage());
            e.printStackTrace();
            return JsonModel.toFail(null, "微信支付异常");
        }
    }
}

支付宝app支付已经在另一篇文章写过了,请自行参考 [字节跳动集成支付宝支付] 里面的内容

字节跳动小程序收银台:ToutiaoMicroApp

 * @description
 */
public class ToutiaoMicroApp {

    private static Logger logger = LoggerFactory.getLogger(ToutiaoMicroApp.class);

    /**
     * 字节跳动小程序收银台,支付宝APP支付、微信H5支付、银行卡支付
     * @return
     */
    public static JSONObject microCheckoutCounter(CheckoutCounterIn in,HttpServletRequest request) {
        Map<String, Object> payParams = new HashMap<>();
        payParams.put("merchant_id", in.getToutiaoMchId());
        payParams.put("app_id", in.getToutiaoMchAppId());
        payParams.put("sign_type", "MD5");
        payParams.put("timestamp", Long.toString(System.currentTimeMillis() / 1000));
        payParams.put("product_code", "pay");
        payParams.put("trade_type", "H5");
        payParams.put("payment_type", "direct");
        payParams.put("version", "2.0");
        payParams.put("out_order_no", in.getOutOrderNo());
        payParams.put("uid", in.getUid());
        payParams.put("total_amount", in.getTotalAmount());
        payParams.put("currency", "CNY");
        payParams.put("subject", in.getSubject());
        payParams.put("body", in.getBody());
        payParams.put("trade_time", Long.toString(System.currentTimeMillis() / 1000));
        payParams.put("valid_time", in.getValidTime());
        payParams.put("notify_url", in.getNotifyUrl());
        //调用支付宝 App 支付所需的支付请求参数(形如 'app_id=xxx&biz_content=xxx...')
        payParams.put("alipay_url", "");
        WxPayParamVo vo = new WxPayParamVo();
        vo.setApiKey(in.getWxApiKey());
        vo.setAppId(in.getWxAppId());
        vo.setAttach(in.getWxAttach());
        vo.setBody(in.getBody());
        vo.setMchId(in.getWxMchId());
        vo.setMoney(in.getWxMoney());
        vo.setNotifyUrl(in.getWxNotifyUrl());
        vo.setServerPath(in.getServerPath());
        vo.setTradeType("MWEB");
        vo.setOutTradeNo(in.getOutOrderNo());
        SortedMap<Object, Object> params = WeixinPay.weixinPayMobile(vo,request);
        //调用微信 H5 支付统一下单接口 返回的 mweb_url 字段值(请注意不要进行 urlencode)
        payParams.put("wx_url", params.get("mwebUrl").toString());
        payParams.put("wx_type", "MWEB");
        String sign = PayCommonUtil.toutiaoAlipaySign(payParams, in.getToutiaoMchSecrect());
        payParams.put("sign", sign);
        payParams.put("ip", request.getRemoteAddr());
        payParams.put("deviceId", Long.toString(System.currentTimeMillis()));
        payParams.put("prepayId", params.get("prepayId").toString());
        logger.info("parameters==={}",payParams);
        return JSONObject.fromObject(payParams);
    }
}

将payParams实体类字段内容返回给前端,前端调用tt.pay唤起收银台支付

前端代码:

  orderInfo: {
    app_id: "800000040005",
    sign_type: "MD5",
    out_order_no: "MicroApp7075638135",
    merchant_id: "1300000004",
    timestamp: "1566720681",
    product_code: "pay",
    payment_type: "direct",
    total_amount: 1,
    trade_type: "H5",
    uid: "2019012211",
    version: "2.0",
    currency: "CNY",
    subject: "microapp test",
    body: "microapp test",
    trade_time: "1566720681",
    valid_time: "300",
    notify_url: "https://tp-pay.snssdk.com/cashdesk/test/paycallback",
    wx_url:
      "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx25161122572189727ea14cfd1832451500&package=2746219290",
    wx_type: "MWEB",
    alipay_url:
      "alipay_sdk=alipay-sdk-java-3.4.27.ALL&app_id=2018061460417275&biz_content=%7B%22body%22%3A%22%E6%B5%8B%E8%AF%95%E8%AE%A2%E5%8D%95%22%2C%22extend_params%22%3A%7B%7D%2C%22out_trade_no%22%3A%2211908250000028453790%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22seller_id%22%3A%222088721387102560%22%2C%22subject%22%3A%22%E6%B5%8B%E8%AF%95%E8%AE%A2%E5%8D%95%22%2C%22timeout_express%22%3A%22599m%22%2C%22total_amount%22%3A%220.01%22%7D&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fapi-test-pcs.snssdk.com%2Fgateway%2Fpayment%2Fcallback%2Falipay%2Fnotify%2Fpay&sign=D2A6ua51os2aIzIH907ppK7Bd9Q2Kk5h7AtKPdudP%2Be%2BNTtAkp0Lfojtgl4BMOIQ3Z7cWyYMx6nk4qbntSx7aZnBhWAcImLbVVr1cmaYAedmrmJG%2B3f8G5TfAZu53ESzUgk02%2FhU1XV0iXRyE8TdEJ97ufmxwsUEc7K0EvwEFDIBCJg73meQtyCRFgCqYRWvmxetQgL7pwfKXpFXjAYsvFrRBas2YGYt689XpBS321g%2BZ8SZ0JOtLPWqhROzEs3dnAtWBW15y3NzRiSNi5rPzah4cWd4SgT0LZHmNf3eDQEHEcPmofoWfnA4ao75JmP95aLUxerMumzo9OwqhiYOUw%3D%3D&sign_type=RSA2&timestamp=2019-08-25+16%3A11%3A22&version=1.0",
    sign: "15aa99cd80878661a4d442b7540bdf96",
    risk_info: '{"ip":"127.0.0.1","device_id":"485737374363263"}'
  },
  service: 1,
  getOrderStatus(res) {
    let { out_order_no } = res;
    return new Promise(function(resolve, reject) {
      // 商户前端根据 out_order_no 请求商户后端查询微信支付订单状态
      tt.request({
        url: "<your-backend-url>",
        success(res) {
          // 商户后端查询的微信支付状态,通知收银台支付结果
          resolve({ code: 0 | 1 | 2 | 3 | 9 });
        },
        fail(err) {
          reject(err);
        }
      });
    });
  },
  success(res) {
    if (res.code == 0) {
      // 支付成功处理逻辑,只有res.code=0时,才表示支付成功
      // 但是最终状态要以商户后端结果为准
    }
  },
  fail(res) {
    // 调起收银台失败处理逻辑
  }
});

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

上一篇:抖音小程序集成支付宝支付

相关文章
|
15天前
|
人工智能 小程序
【一步步开发AI运动小程序】十五、AI运动识别中,如何判断人体站位的远近?
【云智AI运动识别小程序插件】提供人体、运动及姿态检测的AI能力,无需后台支持,具有快速、体验好、易集成等特点。本文介绍如何利用插件判断人体与摄像头的远近,确保人体图像在帧内的比例适中,以优化识别效果。通过`whole`检测规则,分别实现人体过近和过远的判断,并给出相应示例代码。
|
2天前
|
运维 小程序 前端开发
结合圈层营销策略,打造稳定可靠的圈子app系统,圈子小程序!
圈子系统是一种社交平台,用户可按兴趣、职业等创建或加入“圈子”,进行内容发布、讨论和资源共享。开发时需考虑需求分析、技术选型(如PHP、MySQL)、页面设计、功能实现(注册、登录、发布、评论等)、测试优化及运维管理。圈层营销则通过精准化、高端化的方式传递品牌信息,增强客户归属感。圈子小程序基于微信等平台,具备跨平台、便捷性和社交性,开发过程中需明确需求、选择技术框架、设计页面并确保稳定性和流畅性。
|
3天前
|
移动开发 小程序 前端开发
使用php开发圈子系统特点,如何获取圈子系统源码,社交圈子运营以及圈子系统的功能特点,圈子系统,允许二开,免费源码,APP 小程序 H5
开发一个圈子系统(也称为社交网络或社群系统)可以是一个复杂但非常有趣的项目。以下是一些关键特点和步骤,帮助你理解如何开发、获取源码以及运营一个圈子系统。
39 3
|
9天前
|
人工智能 小程序 API
【一步步开发AI运动小程序】十七、如何识别用户上传视频中的人体、运动、动作、姿态?
【云智AI运动识别小程序插件】提供人体、运动、姿态检测的AI能力,支持本地原生识别,无需后台服务,具有速度快、体验好、易集成等优点。本文介绍如何使用该插件实现用户上传视频的运动识别,包括视频解码抽帧和人体识别的实现方法。
|
14天前
|
人工智能 小程序 UED
【一步步开发AI运动小程序】十六、AI运动识别中,如何判断人体站位?
【云智AI运动识别小程序插件】提供人体、运动及姿态检测的AI能力,本地引擎无需后台支持,具备快速、体验好、易集成等优势。本文介绍如何利用插件的`camera-view`功能,通过检测人体站位视角(前、后、左、右),确保运动时的最佳识别率和用户体验。代码示例展示了如何实现视角检查,确保用户正或背对摄像头,为后续运动检测打下基础。
|
14天前
|
移动开发 小程序 PHP
校园圈子论坛系统采取的PHP语音和uni账号开发的小程序APP公众号H5是否只需要4800元?是的,就是只需要4800元
关于校园圈子论坛系统采用PHP语言和uni-app开发的小程序、APP、公众号和H5是否仅需4800元这个问题,实际上很难给出一个确定的答案。这个价格可能受到多种因素的影响
49 8
|
10天前
|
人工智能 小程序 数据处理
uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!
近期,多家康复机构咨询AI运动识别插件是否适用于肢力运动受限患者的康复锻炼。本文介绍该插件在康复锻炼中的应用场景,包括康复运动指导、运动记录、恢复程度记录及过程监测。插件集成了人体检测、姿态识别等功能,支持微信小程序平台,使用便捷,安全可靠,帮助康复治疗更加高效精准。
|
12天前
|
API 开发者
微信native支付对接案例详解
本文详细介绍了微信Native支付的对接流程,包括效果展示、产品介绍、接入前准备、开发指引、API列表、支付通知等,并强调了只有通过微信认证的服务号才能对接微信支付。每年需支付300元认证费用。
25 3
|
16天前
|
人工智能 小程序 JavaScript
【一步步开发AI运动小程序】十四、主包超出2M大小限制,如何将插件分包发布?
本文介绍了如何从零开始开发一个AI运动小程序,重点讲解了通过分包技术解决程序包超过2M限制的问题。详细步骤包括在uni-app中创建分包、配置`manifest.json`和`pages.json`文件,并提供了分包前后代码大小对比,帮助开发者高效实现AI运动功能。
|
2天前
|
小程序 算法 安全
语音交友小程序APP开发/交友小程序软件开发/PC独立后台管理+会员
本方案涵盖语音交友小程序APP开发、交友小程序软件开发及PC独立后台管理加会员系统。小程序功能包括语音匹配、群聊派对、动态广场和个人中心,支持点赞、评论等社交互动,优化用户体验。PC后台管理系统实现用户、内容和数据分析管理,会员系统提供注册、积分、等级等功能,确保数据安全并提升运营效率。
17 0