銀聯(lián)商務(wù)掃碼支付-被掃業(yè)務(wù)

最近在業(yè)務(wù)系統(tǒng)中集成了銀聯(lián)商務(wù)掃碼支付-被掃業(yè)務(wù)產(chǎn)品,最開始還錯(cuò)以為是銀聯(lián)谍倦,殊不知此銀聯(lián)非彼銀聯(lián)耍铜,銀聯(lián)商務(wù)跟銀聯(lián)好像沒什么太大關(guān)系邑闺,網(wǎng)上搜到的資料也寥寥無幾,在此記錄一下集成的過程棕兼。

準(zhǔn)備工作

開放平臺(tái)仿真測(cè)試工具

從銀聯(lián)商務(wù)平臺(tái)網(wǎng)站上下載仿真測(cè)試工具陡舅,直接安裝到谷歌瀏覽器,提示不能使用伴挚,可按照以下方法使用
安裝插件

安裝成功后靶衍,可在瀏覽器右上角看到如下標(biāo)志


安裝成功
仿真測(cè)試工具

sdk是否滿足需求

sdk中封裝了token的獲取,request類中封裝了請(qǐng)求地址茎芋,如果集成被掃業(yè)務(wù)颅眶,需要添加需要調(diào)用的相關(guān)接口的request,或者寫一個(gè)公用的request田弥,在請(qǐng)求前替換請(qǐng)求地址涛酗。


sdk源碼

如果使用request方式,封裝請(qǐng)求參數(shù)是包含在data中偷厦,類似如圖方式
request封裝的請(qǐng)求參數(shù)格式

而實(shí)際被掃業(yè)務(wù)中接口訪問是不需要封裝到data中的商叹,由于我沒有想到更好更簡(jiǎn)潔的方式使用sdk中方法,所以我放棄使用了sdk沪哺,未用sdk中封裝的獲取token的方法

接口對(duì)接

被掃業(yè)務(wù)的接口其實(shí)很簡(jiǎn)單沈自,我總結(jié)我認(rèn)為我集成過程中比較困擾我的點(diǎn)

  1. 因?yàn)榭催^銀聯(lián)接口文檔,在下單的的請(qǐng)求參數(shù)會(huì)添加回調(diào)路徑參數(shù)辜妓,以通知交易結(jié)果枯途,下單返回成功只代表下單成功,并不代表交易已成功籍滴,銀聯(lián)商務(wù)的返回碼是00酪夷,注釋為交易成功,經(jīng)過與技術(shù)支持溝通孽惰,\color{#FF0000}{00即代表已經(jīng)扣錢晚岭,其他的返回碼皆為下單失敗}
  2. 接口文檔的最后有一句“\color{#FF0000}{若支付時(shí)超時(shí)或訂單狀態(tài)不明確},須在當(dāng)天進(jìn)行查詢操作勋功,僅當(dāng)查詢返回明確訂單狀態(tài)坦报,該筆訂單支持以商戶訂單號(hào)的方式進(jìn)行退款】裥”——什么情況下需要調(diào)用查詢接口片择?訂單狀態(tài)不明確又指的什么?\color{#FF0000}{我認(rèn)為超時(shí)骚揍,沒有收到返回碼的都為訂單狀態(tài)不明確}
  3. 請(qǐng)求頭設(shè)置字管,訪問前需要提供一個(gè)token啰挪,直接封裝一個(gè)工具類放在里面即可,下面我把工具類代碼放在下面\color{#FF0000}{參考的[全民付二維碼對(duì)接](https://blog.csdn.net/T_james/article/details/90511775) }
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;


/**
 * @author zhangjz
 * @date 2019/6/3
 */
public class PayUtil {
    private static Logger logger = LoggerFactory.getLogger(PayUtil.class); // 日志記錄
    private static RequestConfig requestConfig = null;
    static
    {
        // 設(shè)置請(qǐng)求和傳輸超時(shí)時(shí)間
        requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build();
    }
    public static String send(String url, String entity, String appId, String appKey){
        CloseableHttpResponse response = null;
        String resStr = null;
        CloseableHttpClient httpClient=null;
        if(isHttps(url)){
            httpClient = HttpClients.custom().setSSLContext(generateSSLContext()).build();
        }else{
            httpClient = HttpClients.createDefault();
        }
        try {
            String authorization = getOpenBodySig(appId, appKey, entity);
            HttpPost httpPost = new HttpPost(url);
            httpPost.setConfig(requestConfig);
            httpPost.addHeader("Authorization", authorization);
            httpPost.setHeader("Content-type", "application/json; charset=utf-8");
            StringEntity se = new StringEntity(entity,"UTF-8");
            se.setContentType("application/json");
            httpPost.setEntity(se);
            response = httpClient.execute(httpPost);
            HttpEntity entity1 = response.getEntity();
            resStr = null;
            if(entity1 != null){
                resStr = EntityUtils.toString(entity1, "UTF-8");
            }
        } catch (Exception e) {
            logger.error("post請(qǐng)求提交失敗:url{}嘲叔,參數(shù){}" ,url,entity, e);
            return null;
        }finally{
            if (response!=null){
                try {
                    httpClient.close();
                    response.close();
                } catch (IOException e) {
                    logger.info("鏈接釋放失敗",e);
                }
            }
        }
        return resStr;
    }
    public static String getOpenBodySig(String appId, String appKey, String body) throws Exception{
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String nonce = UUID.randomUUID().toString().replace("-", "");
        byte[] data = body.getBytes("UTF-8");
//        System.out.println("data:\n" + body);
        InputStream is = new ByteArrayInputStream(data);
        String bodyDigest = testSHA256(is);
        String str1_C = appId+timestamp+nonce+bodyDigest;

        byte[] localSignature = hmacSHA256(str1_C.getBytes(), appKey.getBytes());

        String localSignatureStr = Base64.encodeBase64String(localSignature);
        logger.info("Authorization:\n" + "OPEN-BODY-SIG AppId="+"\""+appId+"\""+", Timestamp="+"\""+timestamp+"\""+", Nonce="+"\""+nonce+"\""+", Signature="+"\""+localSignatureStr+"\"");
        return ("OPEN-BODY-SIG AppId="+"\""+appId+"\""+", Timestamp="+"\""+timestamp+"\""+", Nonce="+"\""+nonce+"\""+", Signature="+"\""+localSignatureStr+"\"");
    }

    private static String testSHA256(InputStream is) {
        try {
            return DigestUtils.sha256Hex(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] hmacSHA256(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data);
    }

    /**
     * 檢測(cè)是否https
     *
     * @param url
     */
    private static boolean isHttps(String url) {
        return url.startsWith("https");
    }

    /*為了訪問https接口*/
    private static SSLContext generateSSLContext(){
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new X509TrustManager[]{new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }}, null);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return sslContext;
    }
}

總結(jié)

第一次集成第三方支付產(chǎn)品亡呵,還是所獲頗豐的,如有說的不到位的地方硫戈,望小伙伴們能夠指正锰什。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掏愁,隨后出現(xiàn)的幾起案子歇由,更是在濱河造成了極大的恐慌,老刑警劉巖果港,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沦泌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡辛掠,警方通過查閱死者的電腦和手機(jī)谢谦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萝衩,“玉大人回挽,你說我怎么就攤上這事⌒梢辏” “怎么了千劈?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)牌捷。 經(jīng)常有香客問我墙牌,道長(zhǎng),這世上最難降的妖魔是什么暗甥? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任喜滨,我火速辦了婚禮,結(jié)果婚禮上撤防,老公的妹妹穿的比我還像新娘虽风。我一直安慰自己,他們只是感情好寄月,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布辜膝。 她就那樣靜靜地躺著,像睡著了一般漾肮。 火紅的嫁衣襯著肌膚如雪厂抖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天初橘,我揣著相機(jī)與錄音验游,去河邊找鬼。 笑死保檐,一個(gè)胖子當(dāng)著我的面吹牛耕蝉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播夜只,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼垒在,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了扔亥?” 一聲冷哼從身側(cè)響起场躯,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旅挤,沒想到半個(gè)月后踢关,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粘茄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年签舞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柒瓣。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡儒搭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芙贫,到底是詐尸還是另有隱情搂鲫,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布磺平,位于F島的核電站魂仍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏褪秀。R本人自食惡果不足惜蓄诽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望媒吗。 院中可真熱鬧仑氛,春花似錦、人聲如沸闸英。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甫何。三九已至出吹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辙喂,已是汗流浹背捶牢。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工鸠珠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秋麸。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓渐排,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親灸蟆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驯耻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容