华为支付
最近项目中集成了华为账号登录与支付的功能,把踩过的坑和过程记录下来。
先看下支付效果图:
支付价格0.01请忽略,因为这是为了测试用的。
刚开始接到这个项目的时候我很奇怪,为什么要集成华为支付呢,原有的微信和支付宝已经能够满足项目需求,难道还有客户偏偏要用华为支付的?看下华为联运人员发来的邮件说明
原来公司是想在华为平台为项目做推广,然后华为的联运应用基础必须要集成华为的账号、支付。然后与我们公司收益三七分成...
ok,那就搞吧。
先说下几个坑点:
2、登录华为账号必须是正式包签名,HMS服务才会起作用(换而言之就是正式包签名下才能登录华为账号)
至于debug模式下如何打正式包,可以看下我这篇博客https://blog.csdn.net/yun382657988/article/details/83928748
3、华为支付的sign签名有可能验证通过不了
这里就需要配合后台开发验证了
4、调试华为推送push,最好在华为手机上调试,其他手机有可能推送不成功(LZ小米5sp手机推送不成功)
一、注册成为华为开发者
接入准备,创建应用、申请HMS服务等巴拉巴拉的一堆就不细说了,可以看详细的华为联运应用文档(注:文档说明,接入华为支付服务的前提,必须成为企业开发者,所以个人的,咱只好歇菜了)给上链接
https://developer.huawei.com/consumer/cn/service/hms/catalog/HwjointOperationAPP.html?page=hmssdk_jointOper_app_prepare
然后还需要完成appid,支付公钥,支付私钥等,这个很好弄,按文档操作
二、集成sdk
集成sdk用Gradle+maven集成方式,依旧看文档操作。这里主要说下集成Agent,通过里面的脚本操作,选择需要的代码拷贝到项目中,这里有个坑,需要注意一下:
保持HMSAgent代码的包的路径和结构不变,什么意思呢?看下图
必须要这样的结构才行,不然会报错!!!!!!
三、配置manifest文件、配置混淆、配置签名等
这里也不做过多说明,根据需求按照文档操作
四、华为账号登录
private void signIn(boolean forceLogin) {
HMSAgent.Hwid.signIn(forceLogin, (rtnCode, signInResult) -> {
if (rtnCode == HMSAgent.AgentResultCode.HMSAGENT_SUCCESS && signInResult != null) {
Map<String, String> params = new HashMap<>();
params.put("username", signInResult.getdisplayName());
if((signInResult.getPhotoUrl()).equals("")){
params.put("head_photo", "https://pan.baidu.com/s/17ZznCBxsde53WfWqFgU2Wg");
}else {
params.put("head_photo", signInResult.getPhotoUrl());
}
params.put("openid", signInResult.getOpenId());
JLogger.d("head_photo:"+signInResult.getPhotoUrl());
uploadUserInfo(params);
} else {
JLogger.e("signIn---ERROR: " + rtnCode);
}
});
}
代码很简单,调用HMSAgent.Hwid.signIn接口即可。这里主要是获取用户的OpenId(应用内用户的唯一标识)、昵称和头像
uploadUserInfo(params)方法是把用户的信息上传到自己项目的服务器上,这里不做过多说明。
非华为手机调试需要安装华为移动服务
全部点击同意。这里建议用华为的手机调试(华为push的集成就不在这说了,网上有很多例子,要用华为的手机,lz用的小米的push推送老是不成功哦,这个是我踩的坑二,哈哈)
五、华为支付
支付的代码也不多,主要是调用HMSAgent.Pay.pay接口,贴上代码
package com.jm.ec.main.pay;
import Android.app.Activity;
import android.content.SharedPreferences;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.jsonobject;
import com.huawei.android.hms.agent.HMSAgent;
import com.huawei.android.hms.agent.pay.PaySignUtil;
import com.huawei.hms.support.api.entity.pay.orderrequest;
import com.huawei.hms.support.api.entity.pay.PayReq;
import com.huawei.hms.support.api.entity.pay.PayStatusCodes;
import com.huawei.hms.support.api.pay.PayResultInfo;
import com.jm.core.delegates.JumeiDelegate;
import com.jm.core.net.RestClient;
import com.jm.core.util.log.JLogger;
import com.jm.ec.constant.Jconstants;
import com.jm.ec.event.StartOrderEvent;
import com.jm.ec.main.design.IEvnValues;
import org.greenrobot.eventbus.EventBus;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.util.Date;
import static com.jm.core.app.Jumei.getActivity;
public class HuaWeiPay {
private Activity mActivity = null;
private static final String pay_priv_key = IEvnValues.pay_priv_key;
private static final String pay_pub_key = IEvnValues.pay_pub_key;
private static final String appId = IEvnValues.appId;
private static final String cpId = IEvnValues.cpId;
private static int RESULT ;
private static String Amount = null;
private static String ORDERID = null;
private static String REQUESTID = null;
private static String NOTIFYTIME = null;
private static String USERNAME= null;
private static String PAYNAME = "";
private HuaWeiPay(JumeiDelegate delegate) {
this.mActivity = delegate.getProxyActivity();
}
public static HuaWeiPay create(JumeiDelegate delegate) {
return new HuaWeiPay(delegate);
}
/**
* 普通支付示例
*/
public static void pay(float amount, String orderNumber, String name) {
PayReq payReq = createPayReq(amount,orderNumber,name);
HMSAgent.Pay.pay(payReq, (int retCode, PayResultInfo payInfo) -> {
if (retCode == HMSAgent.AgentResultCode.HMSAGENT_SUCCESS && payInfo != null) {
boolean checkRst = PaySignUtil.checkSign(payInfo, pay_pub_key);
JLogger.d("pay: onResult: pay success and checksign=" + checkRst);
if (checkRst) {
// 支付成功并且验签成功,发放商品
RESULT = payInfo.getReturnCode();
AMOUNT = payInfo.getAmount();
ORDERID = payInfo.getOrderID();
REQUESTID = orderNumber;
NOTIFYTIME = payInfo.getTime();
USERNAME = payInfo.getUserName();
PAYNAME = name;
RestClient.builder()
.url("这个主要是获取华为sign的接口,因为上传payInfo.getSign()获取的sign,后台那边死活通过不了,于是写了个接口获取华为sign")
.params("result",payInfo.getReturnCode())
.params("amount",payInfo.getAmount())
.params("orderId",payInfo.getOrderID())
.params("requestId",orderNumber)
.params("notifyTime",payInfo.getTime())
.params("userName",payInfo.getUserName())
.params("productName",name)
.params("payType",4)
.params("sign",payInfo.getSign())
.success(response -> handleresultHuawei(response))
.build()
.post();
JLogger.d("PayResultInfo================="+payInfo.getSign());
} else {
// 签名失败,需要查询订单状态:对于没有服务器的单机应用,调用查询订单接口查询;其他应用到开发者服务器查询订单状态。
getPayDetail(orderNumber);
}
} else if (retCode == HMSAgent.AgentResultCode.ON_ACTIVITY_RESULT_ERROR
|| retCode == PayStatusCodes.PAY_STATE_TIME_OUT
|| retCode == PayStatusCodes.PAY_STATE_NET_ERROR) {
// 需要查询订单状态:对于没有服务器的单机应用,调用查询订单接口查询;其他应用到开发者服务器查询订单状态。
getPayDetail(orderNumber);
} else {
JLogger.d("pay: onResult: pay fail=" + retCode);
// 其他错误码意义参照支付api参考
}
});
// 将requestid缓存,供查询订单
addRequestIdTocache(payReq.getRequestId());
}
private static void handleResultHuawei(String response) {
final JSONObject jsonObject = JSON.parseObject(response);
if (JConstants.OK.equals(jsonObject.getString("code"))) {
JSONObject data = jsonObject.getJSONObject("data");
final String sign = data.getString("sign");
JLogger.d(sign);
RestClient.builder()
.url("这个接口是验证获取的华为singn")
.params("result",RESULT)
.params("amount",AMOUNT)
.params("orderId",ORDERID)
.params("requestId",REQUESTID)
.params("notifyTime",NOTIFYTIME)
.params("userName",USERNAME)
.params("productName",PAYNAME)
.params("payType",4)
.params("appSign",sign)//就是这个appSign咯
.success(response1 -> handleResultSign(response1))
.build()
.post();
}
}
private static void handleResultSign(String response) {
JLogger.json(response);
final JSONObject jsonObject = JSON.parseObject(response);
if (JConstants.OK.equals(jsonObject.getString("code"))) {
updatePaySuccessUI();
}
}
private static void addRequestIdToCache(String requestId) {
SharedPreferences sp = getActivity().getSharedPreferences("pay_request_ids", 0);
sp.edit().putBoolean(requestId, false).commit();
}
/**
* 创建普通支付请求对象
* @param totalAmount 要支付的金额
* @param orderNumber
* @return 普通支付请求对象
*/
private static PayReq createPayReq(float totalAmount, String orderNumber, String name) {
PayReq payReq = new PayReq();
/**
* 生成requestId
*/
DateFormat format = new java.text.simpledateformat("yyyyMMddhhmmssSSS");
int random= new SecureRandom().nextint() % 100000;
random = random < 0 ? -random : random;
String requestId = format.format(new Date());
requestId = string.format("%s%05d", requestId, random);
/**
* 生成总金额 | Generate Total Amount
*/
String amount = String.format("%.2f", totalAmount);
//商品名称
payReq.productName = name;
//商品描述
payReq.productDesc = "发型设计、形象设计、脸型分析";
// 商户ID,来源于开发者联盟,也叫“支付id”
payReq.merchantId = cpId;
// 应用ID,来源于开发者联盟
payReq.applicationID = appId;
// 支付金额 | Amount paid
payReq.amount = amount;
// 支付订单号
payReq.requestId = orderNumber;
// 国家码 | Country code
payReq.country = "CN";
//币种
payReq.currency = "CNY";
// 渠道号
payReq.sdkChannel = 1;
// 回调接口版本号
payReq.urlVer = "2";
// 商户名称,必填,不参与签名。会显示在支付结果页面
payReq.merchantName = "合肥聚美网络科技有限公司";
//分类,必填,不参与签名。该字段会影响风控策略
// X4:主题,X5:应用商店, X6:游戏,X7:天际通,X8:云空间,X9:电子书,X10:华为学习,X11:音乐,X12 视频,
// X31 话费充值,X32 机票/酒店,X33 电影票,X34 团购,X35 手机预购,X36 公共缴费,X39 流量充值
payReq.serviceCatalog = "X5";
//商户保留信息,选填不参与签名,支付成功后会华为支付平台会原样 回调CP服务端
payReq.extReserved = "Here to fill in the Merchant reservation information";
//对单机应用可以直接调用此方法对请求信息签名,非单机应用一定要在服务器端储存签名私钥,并在服务器端进行签名操作。
// 在服务端进行签名的cp可以将getStringForSign返回的待签名字符串传给服务端进行签名
payReq.sign = PaySignUtil.rsaSign(PaySignUtil.getStringForSign(payReq), pay_priv_key);
return payReq;
}
private static void getPayDetail(final String reqId) {
OrderRequest or = new OrderRequest();
JLogger.d("checkPay: begin=" + reqId);
or.setRequestId(reqId);
or.setTime(String.valueOf(system.currenttimemillis()));
or.setKeyType("1");
or.setMerchantId(cpId);
or.sign = PaySignUtil.rsaSign(PaySignUtil.getStringForSign(or), pay_priv_key);
HMSAgent.Pay.getOrderDetail(or, (retCode, checkPayResult) -> {
JLogger.d("checkPay: requId="+reqId+" retCode=" + retCode);
if (checkPayResult != null && checkPayResult.getReturnCode() == retCode) {
// 处理支付业务返回码 | Processing Payment Business return code
if (retCode == HMSAgent.AgentResultCode.HMSAGENT_SUCCESS) {
boolean checkRst = PaySignUtil.checkSign(checkPayResult, pay_pub_key);
if (checkRst) {
// 支付成功,发放对应商品
JLogger.d("checkPay: Pay successfully, distribution of goods");
} else {
// 验签失败,当支付失败处理
JLogger.d("checkPay: failed to verify signature, pay failed");
}
// 不需要再查询 | No more queries
removeCacheRequestId(checkPayResult.getRequestId());
} else if (retCode == PayStatusCodes.ORDER_STATUS_HANDLING
|| retCode == PayStatusCodes.ORDER_STATUS_UNTREATED
|| retCode == PayStatusCodes.PAY_STATE_TIME_OUT) {
// 未处理完,需要重新查询。如30分钟后再次查询。超过24小时当支付失败处理
JLogger.d("checkPay: Pay failed. errorCode="+retCode+" errMsg=" + checkPayResult.getReturnDesc());
} else if (retCode == PayStatusCodes.PAY_STATE_NET_ERROR) {
// 网络失败,需要重新查询
JLogger.d("checkPay: A network problem caused the payment to fail. errorCode="+retCode+" errMsg=" + checkPayResult.getReturnDesc());
} else {
// 支付失败,不需要再查询
JLogger.d("checkPay: Pay failed. errorCode="+retCode+" errMsg=" + checkPayResult.getReturnDesc());
removeCacheRequestId(reqId);
}
} else {
// 没有结果回来,需要重新查询。如30分钟后再次查询。超过24小时当支付失败处理
JLogger.d("checkPay: Pay failed. errorCode="+retCode);
}
});
}
private static void removeCacheRequestId(String reqId) {
SharedPreferences sp = getActivity().getSharedPreferences("pay_request_ids", 0);
sp.edit().remove(reqId).commit();
}
private static void updatePaySuccessUI() {
EventBus.getDefault().post(new StartOrderEvent());//发送消息给主线程,跳转到订单详情页
}
}
上面的支付代码,在华为demo里也有,建议,先把私钥、公钥、AppID、cpid放到demo里看能否运行起来,再接入到项目中,省时省力。其实华为文档上建议把私钥放在开发者的服务器端,客户端请求,私钥生成这些处理放在服务器端处理,这样是为了安全起见哦。我们这边后端没写,只能放里面了。
这里支付做了简单的封装处理,需要支付的时候HuaWeiPay.pay(Float.parseFloat(price),orderNumber,name);写上这行代码,传进来的参数分别是价格、订单编号、和支付产品的名字
以上即是华为账号登录与华为支付了,这里总结起来容易,前几天弄的时候可是踩了不少坑,哈哈。
相关阅读
互联网迅速发展至今日,大家最担心的就是信息安全问题了。很多人都碰到过自己的信息被一些平台非法售卖的事情,例如:大家注册了某个房
自从昨天支付宝开启了集福卡的活动以后,出门总是能够听到人们在一起议论今天扫到了什么福卡或者是还差什么福卡的话题,毕竟每到春节
说到支付宝,相信每个拥有智能机的用户手机里面应该都下载有支付宝,支付宝的功能就不用多说了吧,出门不带钱全靠支付宝,不管你是超市买
前言: service服务,能够使得应用程序即使在关闭的情况下仍然可以在后台继续执行。后台功能属于四大组件之一,其重要程度不言而喻,那
支付宝怎么赚钱啊,最近支付宝又开始了活动,扫红包领现金了!支付宝现在普遍被大众使用,在人们的观念中,每年春节的时候会用支付宝抢福,抢