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

您所在的位置:网站首页 微信小程序为啥不能用支付宝 抖音小程序开发 唤起收银台支付(可以选择支付宝APP支付或微信H5支付)

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

2024-06-29 13:15| 来源: 网络整理| 查看: 265

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

1、后台管理地址:

https://developer.toutiao.com/

2、开发文档地址:

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

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

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

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

代码实现

实体类:TTPayParamIn

代码语言:javascript复制/** * @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

代码语言:javascript复制 /** * @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

代码语言:javascript复制 /** * @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 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 parameters){ StringBuffer sb = new StringBuffer(); sb.append(""); 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(""+""); }else { sb.append(""+v+""); } } sb.append(""); 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 ""; } //对请求参数进行组装+key public static String toutiaoAlipaySign(Map 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 map) { ArrayList list = new ArrayList(); for (Map.Entry 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

代码语言:javascript复制/** * @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;"); // 当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

代码语言:javascript复制/** * @description */ public class XMLUtil { /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } Map 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(""); if(!list.isEmpty()) { sb.append(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append(""); } } return sb.toString(); } }

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

代码语言:javascript复制/** * @description */ public class WeixinPay { public static Logger logger = LoggerFactory.getLogger(WeixinPay.class); /** * 微信支付 * @param wxPayParamVo * @param request * @return */ public static SortedMap weixinPayMobile(WxPayParamVo wxPayParamVo, HttpServletRequest request) { SortedMap params = new TreeMap(); try { Double moneys = wxPayParamVo.getMoney() * 100; // 我们发送给微信服务器的参数是xml格式的string,微信返回来的信息也是xml格式的string // 获取package包 SortedMap parameters = new TreeMap(); 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 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

代码语言:javascript复制/** * @description */ public class ToutiaoMicroApp { private static Logger logger = LoggerFactory.getLogger(ToutiaoMicroApp.class); /** * 字节跳动小程序收银台,支付宝APP支付、微信H5支付、银行卡支付 * @return */ public static JSONObject microCheckoutCounter(CheckoutCounterIn in,HttpServletRequest request) { Map 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 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唤起收银台支付

前端代码:

代码语言:javascript复制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¬ify_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×tamp=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: "", success(res) { // 商户后端查询的微信支付状态,通知收银台支付结果 resolve({ code: 0 | 1 | 2 | 3 | 9 }); }, fail(err) { reject(err); } }); }); }, success(res) { if (res.code == 0) { // 支付成功处理逻辑,只有res.code=0时,才表示支付成功 // 但是最终状态要以商户后端结果为准 } }, fail(res) { // 调起收银台失败处理逻辑 } });

上一篇:字节跳动集成支付宝支付



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3