什么都别说,先看效果:
字节跳动也开放了小程序给商家接入,可以在旗下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
/**
* @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;
}
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
请求工具类: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;
}
}
微信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);
}
}