接入華爲支付

接入華爲支付

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

<meta-data  
    android:name="com.huawei.hms.client.appid"  
    <!-- value的值“xxx”用實際申請的應用ID替換,來源於開發者聯盟網站應用的服務詳情。-->  
    android:value="appid=xxx">  
</meta-data> 

記住裏面是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<T extends BaseBuyIView,P extends BaseBuyPresenter<T>> extends BaseActivity<T,P> 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<String, Object> paramsa = new HashMap<String, Object>();
                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> 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<PayResult> {

    @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<String, Object>();
    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<String, Object> params) {
    //對參數按照key做升序排序,對map的所有value進行處理,轉化成string類型
    //拼接成key=value&key=value&....格式的字符串
    StringBuffer content = new StringBuffer();
    // 按照key做排序
    List<String> keys = new ArrayList<String>(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;
}}

好了華爲支付完了,有問題請加羣:142739277或者加我QQ:893151960

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章