1、华为支付相对微信支付又复杂点,同样包名,签名,appId都必须正确,不能修改
2、配置内容也多点,不过基本按照文档说明认真配置也是没问题的
3、华为支付是没demo源码参考,只有文档的示例代码,其他第三方一般都会有demo的
官方开发文档
https://developer.huawei.com/consumer/cn/service/hms/catalog/huaweiiap_oversea.html?page=hmssdk_huaweiiap_devprepare_oversea
按照文档一步步来,千万不要跳步骤,要不出问题不好排查
可能出问题的地方
在清单的application节点下增加APPID
android:value="appid=xxx">
记住里面是appid=xxx而不是去掉xxx
初始化Agent
1.在Application类中的onCreate方法中初始化HMS Agent套件
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HMSAgent.init(this);
}}
2.请务必在应用启动后的首个activity的onCreate方法中调用connect接口,确保HMS SDK和HMS APK的连接
HMSAgent.connect(this, new ConnectHandler() {
@Override
public void onConnect(int rst) {
showLog("HMS connect end:" + rst);
}});
注意是启动的第一个activity,要不会上架审核不成功的
调起华为支付
关键方法HuaweiPay.HuaweiPayApi.pay();
只好建个中间的Activity来集成,因为支付返回结果都是在onActivityResult获取。如果只有一个界面有华为支付的,就没必要了。
中间华为支付activity
示例代码:
public abstract class HuaWeiActivity> extends BaseActivity implements ISetHw {
private static final String TAG = HuaWeiActivity.class.getName();
public HuaweiApiClient client;
private String outTradeNo;
//启动参数,区分startactivityforresult的处理结果
private final int REQ_CODE_PAY = 4001;
//作用同startactivityforresult方法中的requestcode
private static final int REQUEST_HMS_RESOLVE_ERROR = 1000;
private ShowPayDialog showPayDialog;
//初始化init华为支付
private void initClient() {
//-------------------华为支付----------------------
if(null == client){
client = new HuaweiApiClient.Builder(this)
.addApi(HuaweiPay.PAY_API)
.addOnConnectionFailedListener(this)
.addConnectionCallbacks(this)
.build();
}
client.connect(this);
}
@Override
public void onResume() {
super.onResume();
initClient();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (null != client) {
client.disconnect();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
LogUtils.i("reCode", requestCode + " " + resultCode + "");
if (requestCode == REQUEST_HMS_RESOLVE_ERROR) {
if (resultCode == Activity.RESULT_OK) {
int result = data.getIntExtra(EXTRA_RESULT, 0);
if (result == ConnectionResult.SUCCESS) {
LogUtils.i(TAG, "错误成功解决");
if (!client.isConnecting() && !client.isConnected()) {
client.connect(this);
}
} else if (result == ConnectionResult.CANCELED) {
LogUtils.i(TAG, "解决错误过程被用户取消");
} else if (result == ConnectionResult.INTERNAL_ERROR) {
LogUtils.i(TAG, "发生内部错误,重试可以解决");
//CP可以在此处重试连接华为移动服务等操作,导致失败的原因可能是网络原因等
} else {
LogUtils.i(TAG, "未知返回码");
}
} else {
LogUtils.i(TAG, "调用解决方案发生错误");
}
} else if (requestCode == REQ_CODE_PAY) {
//当返回值是-1的时候表明用户支付调用成功
if (resultCode == Activity.RESULT_OK) {
//获取支付完成信息
PayResultInfo payResultInfo = HuaweiPay.HuaweiPayApi.getPayResultInfoFromIntent(data);
if (payResultInfo != null) {
Map paramsa = new HashMap();
if (PayStatusCodes.PAY_STATE_SUCCESS == payResultInfo.getReturnCode()) {
//mHuaWeiPayPresenter.getHwPayNotify(b_tag, payResultInfo.getRequestId(), payResultInfo.getReturnCode(), success);
payHwSuccessNotify(payResultInfo.getReturnCode());
//paySuccess();
closeDialog();
} else if (PayStatusCodes.PAY_STATE_CANCEL == payResultInfo.getReturnCode()) {
//支付失败,原因是用户取消了支付,可能是用户取消登录,或者取消支付
Log.i(TAG, "支付失败:用户取消" + payResultInfo.getErrMsg());
payHwFailNotify();
} else {
//支付失败,其他一些原因
Log.i(TAG, "支付失败:" + payResultInfo.getErrMsg() + payResultInfo.getReturnCode());
payHwFailNotify();
}
} else {
//支付失败
payHwFailNotify();
}
} else {
//当resultCode 为0的时候表明用户未登录,则CP可以处理用户不登录事件
Log.i(TAG, "resultCode为0, 用户未登录 CP可以处理用户不登录事件");
}
}
}
protected abstract void payHwSuccessNotify(int payCode);
protected abstract void payHwFailNotify();
@Override
public void onConnected() {
}
@Override
public void onConnectionSuspended(int i) {
//HuaweiApiClient异常断开连接, if 括号里的条件可以根据需要修改
if (!this.isDestroyed() && !this.isFinishing()) {
client.connect(this);
}
LogUtils.i(TAG, "HuaweiApiClient 连接异常断开成功");
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
LogUtils.i(TAG, "HuaweiApiClient连接失败,错误码:" + connectionResult.getErrorCode());
if (HuaweiApiAvailability.getInstance().isUserResolvableError(connectionResult.getErrorCode())) {
final int errorCode = connectionResult.getErrorCode();
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
// 此方法必须在主线程调用, xxxxxx.this 为当前界面的activity
HuaweiApiAvailability.getInstance().resolveError(HuaWeiActivity.this, errorCode, REQUEST_HMS_RESOLVE_ERROR);
}
});
} else {
//其他错误码请参见API文档
}
}
/**
* 发起支付流程,开发者可以直接参照该方法写法
*/
protected void hwPay(HashMap params, String sign) {
if (!client.isConnected()) {
LogUtils.i(TAG, "支付失败,原因:HuaweiApiClient未连接");
client.connect(this);
return;
}
PendingResult payResult = HuaweiPay.HuaweiPayApi.pay(client, HwPayUtils.createPayReq(params, sign));
payResult.setResultCallback(new PayResultCallback());
}
/**
* 弹框相关
*
* @param showPayDialog
*/
public void setShowDialog(ShowPayDialog showPayDialog) {
this.showPayDialog = showPayDialog;
}
/**
* 支付接口调用的回调处理
* 只有当处理结果中的返回码为 PayStatusCodes.PAY_STATE_SUCCESS的时候,CP需要继续调用支付
* 否则就需要处理支付失败结果
*/
private class PayResultCallback implements ResultCallback {
@Override
public void onResult(PayResult result) {
//支付鉴权结果,处理result.getStatus()
Status status = result.getStatus();
if (PayStatusCodes.PAY_STATE_SUCCESS == status.getStatusCode()) {
//当支付回调 返回码为0的时候,表明支付流程正确,CP需要调用startResolutionForResult接口来进行后续处理
//支付会先判断华为帐号是否登录,如果未登录,会先提示用户登录帐号。之后才会进行支付流程
try {
status.startResolutionForResult(HuaWeiActivity.this, REQ_CODE_PAY);
} catch (IntentSender.SendIntentException e) {
LogUtils.i(TAG, "启动支付失败" + e.getMessage());
}
} else {
LogUtils.i(TAG, "支付失败,原因 :" + status.getStatusCode());
}
}
}
public void closeDialog() {
if (null != showPayDialog) {
showPayDialog.dialogDismiss();
}
}}
仅仅是示例,把不用的删掉即可,
其中HuaweiPay.HuaweiPayApi.pay是调起华为支付,
PayResultCallback是是否能正常调起华为支付,
onActivityResult是调起后支付成功还是失败
HwPayUtils类
public class HwPayUtils {
private static final String TAG = "HwPayUtils";
/**
* 获取华为的appId
*
* @param context
* @return
*/
public static String getAppId(Context context) {
String value = "";
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
value = appInfo.metaData.getString("com.huawei.hms.client.appid");
String[] appidValue = value.split("=");
return appidValue[appidValue.length-1];
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return value;
}
/**
* 生成支付信息map 包含
* HwPayConstant.KEY_MERCHANTID 必选参数 商户id,开发者联盟网站生成的支付ID
* HwPayConstant.KEY_APPLICATIONID 必选参数 应用的appid,开发者联盟网站生成
* HwPayConstant.KEY_AMOUNT 必选参数 支付金额 string类型,精确到小数点后2位 比如 20.00
* HwPayConstant.KEY_PRODUCTNAME 必选参数 商品名称 此名称将会在支付时显示给用户确认 注意:该字段中不能包含特殊字符,包括# " & / ? $ ^ *:) \ < > ,
* HwPayConstant.KEY_PRODUCTDESC 必选参数 商品描述 注意:该字段中不能包含特殊字符,包括# " & / ? $ ^ *:) \ < > , |
* HwPayConstant.KEY_REQUESTID 必选参数 请求订单号。其值由商户定义生成,用于标识一次支付请求,每次请求需唯一,不可重复。
* 支付平台在服务器回调接口中会原样返回requestId的值。注意:该字段中不能包含特殊字符,包括# " & / ? $ ^ *:) \ < > , .以及中文字符
* HwPayConstant.KEY_SDKCHANNEL 必选参数 渠道信息。 取值如下:0 代表自有应用,无渠道 1 代表应用市场渠道 2 代表预装渠道 3 代表游戏中心渠道
* HwPayConstant.KEY_URLVER 可选参数 回调接口版本号。如果传值则必须传2, 额外回调信息,具体参考接口文档
* HwPayConstant.KEY_URL 可选参数 支付结果回调URL. 华为服务器收到后检查该应用有无在开发者联盟配置回调URL,如果配置了则使用应用配置的URL,否则使用此url
* 作为该次支付的回调URL,建议直接 以配置在 华为开发者联盟的回调URL为准
* HwPayConstant.KEY_COUNTRY 可选参数 国家码.建议无特殊需要,不传
* HwPayConstant.KEY_CURRENCY 可选参数 币种 选填.建议无特殊需要不传此参数。目前仅支持CNY,默认CNY
*/
public static HashMap getPayInfo(PayHwOrderResult result) {
HashMap params = new HashMap();
PayHwOrderResult.DataBean obj = result.getData();
params.put(HwPayConstant.KEY_MERCHANTID, obj.getMerchantId());
params.put(HwPayConstant.KEY_APPLICATIONID, obj.getAppId());
params.put(HwPayConstant.KEY_AMOUNT, obj.getAmount());
params.put(HwPayConstant.KEY_PRODUCTNAME, obj.getProductName());
params.put(HwPayConstant.KEY_PRODUCTDESC, obj.getProductDesc());
params.put(HwPayConstant.KEY_REQUESTID, obj.getOutTradeNo());
params.put(HwPayConstant.KEY_SDKCHANNEL, obj.getSdkChannel());
params.put(HwPayConstant.KEY_URLVER, obj.getUrlver());
params.put(HwPayConstant.KEY_URL, obj.getUrl());
//不需要签名参数
params.put(HwPayConstant.KEY_MERCHANTNAME, obj.getMerchantName());
params.put(HwPayConstant.KEY_SERVICECATALOG, obj.getServiceCatalog());
params.put(HwPayConstant.KEY_EXTRESERVED, obj.getExtReserved());
return params;
}
/**
* 封装json参数给后台
*
* @param params
* @return
*/
public static String paramJson(HashMap params) {
String value = "";
try {
JSONObject mJsonobjData = new JSONObject();
mJsonobjData.put(HwPayConstant.KEY_MERCHANTID, params.get(HwPayConstant.KEY_MERCHANTID));
mJsonobjData.put(HwPayConstant.KEY_APPLICATIONID, params.get(HwPayConstant.KEY_APPLICATIONID));
mJsonobjData.put(HwPayConstant.KEY_AMOUNT, params.get(HwPayConstant.KEY_AMOUNT));
mJsonobjData.put(HwPayConstant.KEY_PRODUCTNAME, params.get(HwPayConstant.KEY_PRODUCTNAME));
mJsonobjData.put(HwPayConstant.KEY_PRODUCTDESC, params.get(HwPayConstant.KEY_PRODUCTDESC));
mJsonobjData.put(HwPayConstant.KEY_REQUESTID, params.get(HwPayConstant.KEY_REQUESTID));
mJsonobjData.put(HwPayConstant.KEY_SDKCHANNEL, params.get(HwPayConstant.KEY_SDKCHANNEL));
mJsonobjData.put(HwPayConstant.KEY_URLVER, params.get(HwPayConstant.KEY_URLVER));
mJsonobjData.put(HwPayConstant.KEY_URL, params.get(HwPayConstant.KEY_URL));
value = mJsonobjData.toString();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return value;
}
/**
* 生成PayReq对象,用来在进行支付请求的时候携带支付相关信息
* payReq订单参数需要商户使用在华为开发者联盟申请的RSA私钥进行签名,强烈建议将签名操作在商户服务端处理,避免私钥泄露
*/
public static PayReq createPayReq(HashMap params, String sign) {
PayReq payReq = new PayReq();
//商品名称
payReq.productName = (String) params.get(HwPayConstant.KEY_PRODUCTNAME);
//商品描述
payReq.productDesc = (String) params.get(HwPayConstant.KEY_PRODUCTDESC);
// 商户ID:来源于开发者联盟的“支付ID”
payReq.merchantId = (String) params.get(HwPayConstant.KEY_MERCHANTID);
// 应用ID
payReq.applicationID = (String) params.get(HwPayConstant.KEY_APPLICATIONID);
// 支付金额
payReq.amount = (String) params.get(HwPayConstant.KEY_AMOUNT);
// 商户订单号:开发者在支付前生成,用来唯一标识一次支付请求
payReq.requestId = (String) params.get(HwPayConstant.KEY_REQUESTID);
// 渠道号
payReq.sdkChannel = (Integer) params.get(HwPayConstant.KEY_SDKCHANNEL);
// 回调接口版本号
payReq.urlVer = (String) params.get(HwPayConstant.KEY_URLVER);
payReq.url = (String) params.get(HwPayConstant.KEY_URL);
LogUtils.i("hwPayUrl",payReq.url);
LogUtils.i(TAG, payReq.productName + " " + payReq.productDesc + " " + payReq.merchantId + " " + payReq.applicationID + " "
+ payReq.amount + " " + payReq.requestId + " " + payReq.sdkChannel + " " + payReq.getUrlVer());
//以上信息按照一定规则进行签名,建议CP在服务器端储存签名私钥,并在服务器端进行签名操作。
payReq.sign = sign;
// 商户名称,必填,不参与签名。开发者注册的公司名
payReq.merchantName = (String) params.get(HwPayConstant.KEY_MERCHANTNAME);
//分类,必填,不参与签名。该字段会影响风控策略
// X4:主题,X5:应用商店, X6:游戏,X7:天际通,X8:云空间,X9:电子书,X10:华为学习,X11:音乐,X12 视频,
// X31 话费充值,X32 机票/酒店,X33 电影票,X34 团购,X35 手机预购,X36 公共缴费,X39 流量充值
payReq.serviceCatalog = (String) params.get(HwPayConstant.KEY_SERVICECATALOG);
//商户保留信息,选填不参与签名,支付成功后会华为支付平台会原样 回调CP服务端
payReq.extReserved = (String) params.get(HwPayConstant.KEY_EXTRESERVED);
return payReq;
}
/**
* 将商户id,应用id, 商品名称,商品说明,支付金额,订单号,渠道号,回调地址版本号等信息按照key值升序排列后
* 以key=value并以&的方式连接起来生成待签名的字符串
*
* @return
*/
public static String getNoSign(Map params) {
//对参数按照key做升序排序,对map的所有value进行处理,转化成string类型
//拼接成key=value&key=value&....格式的字符串
StringBuffer content = new StringBuffer();
// 按照key做排序
List keys = new ArrayList(params.keySet());
Collections.sort(keys);
String value = null;
Object object = null;
for (int i = 0; i < keys.size(); i++) {
String key = (String) keys.get(i);
object = params.get(key);
if (object instanceof String) {
value = (String) object;
} else {
value = String.valueOf(object);
}
if (value != null) {
content.append((i == 0 ? "" : "&") + key + "=" + value);
} else {
continue;
}
}
//待签名的字符串
String signOri = content.toString();
return signOri;
}
/**
* 使用开发者联盟提供的支付公钥对支付成功结果中的签名信息进行验证
* 如果签名验证成功,则表明支付流程正确
* 如果签名验证不成功,那么支付已经成功,但是签名有误,CP需要到服务器上查询支付情况
*
* @param content
* @param sign
* @return
*/
public static boolean doCheck(String content, String sign) {
//开发者联盟提供的支付公钥
String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAje6cNmwjC9wY3QuZ/YTMzqwqvDFRzlswS3evaRsuUkz9PnAXqea8rG2/JHKj1zASfbAXkdGVnojmlGuhiUTleYgZK6Hq7crrm77WtM25TgWQ15vBZp6inuqnqwdGiXrJdqSng5LGkIyrMIC1PLdSeYtb76dW/Y0fAAz277X+WK10nzL2RnVSZAAjZnnZVTaDJhWCy3uY90YsiLbaxXVghuNRuBJ+vSave61Ut4yOA5AGK1QwQPq0/c91MLQgkLPwmncz0BGK3+H7pYIN/ceQ1033tL7WZE3hHj2lgwRu0KTfk7d96AC0WMG2lk9BKHAZCxMswC4bXKjDa55MPb8JwQIDAQAB";
//使用加密算法规则
String SIGN_ALGORITHMS = "SHA256WithRSA";
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
signature.initVerify(pubKey);
signature.update(content.getBytes("utf-8"));
boolean bverify = signature.verify(Base64.decode(sign, Base64.DEFAULT));
return bverify;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "doCheck NoSuchAlgorithmException" + e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "doCheck InvalidKeySpecException" + e);
} catch (InvalidKeyException e) {
Log.e(TAG, "doCheck InvalidKeyException" + e);
} catch (SignatureException e) {
Log.e(TAG, "doCheck SignatureException" + e);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "doCheck UnsupportedEncodingException" + e);
}
return false;
}}