支付寶小程序擴展能力 AntBuilder 會員接入指南

2020-09-19 10:44 更新

操作手冊

本產(chǎn)品暫為定向輸出,若有對外輸出訴求或需求洽談事項,請聯(lián)系合作伙伴技術(shù)組:partner-booster@service.alipay.com

業(yè)務(wù)系統(tǒng)配置

不打通 CRM(默認)

只需要配置卡管系統(tǒng)的公網(wǎng) URL(就是小程序后端 web-mini 的公網(wǎng) URL)。

image.png

打通 CRM

配置項:

請前往 AntBuilder 安裝目錄 antbuilder-installer/application/web-mini/config 以及 antbuilder-installer/application/web-management/config,編輯 application-prod.yml 文件,參考下列實例底部配置如下內(nèi)容。

image.png

前置條件:

CRM 提供以下接口,接口格式參考下面技術(shù)接入手冊:

  1. 開卡信息接口:用戶領(lǐng)卡時調(diào)用,傳入用戶 alipay UID + 領(lǐng)卡表單信息,換取用戶會員卡號、積分、等級信息。
  2. 卡信息變動接口:信息變更(新增、刪除)時調(diào)用,以同步信息,如果不需要刪除,接口中可以不做操作。

為了保證接口調(diào)用安全性,CRM 和web-mini調(diào)用之間有安全驗證

  1. web-mini 調(diào)用 CRM(開卡信息接口和卡信息變動接口),在 HEADER 中加入 token,CRM 可以驗證 token 是否一致。
  2. CRM 調(diào)用 mini-core(更新積分和模板):使用 RSA2 加簽驗簽,CRM 使用私鑰加簽,mini-core 使用公鑰驗簽。

image.png

配置會員卡

  1. 配置會員卡應(yīng)用。會員卡應(yīng)用建議使用 WEB 應(yīng)用,方便后續(xù)會員卡單獨對外漏出。image.png image.png

  1. 創(chuàng)建會員卡模板。 image.png image.png

  1. 配置基本信息。為創(chuàng)建的模板取名字,選擇會員卡有效期,適用門店列表。image.png

  1. 配置卡樣式。主要配置卡的圖片,顯示的名稱,以及字體顏色,卡碼類型(動態(tài)碼需配置刷新時間)。 說明:這一步可以驗證支付寶應(yīng)用配置是否正確,如果上傳失敗,請前往文檔最后的常見問題。image.png

  1. 配置欄位。分為標準欄位和自定義欄位。標準欄位,目前僅支持積分。 image

  1. 行動點配置。行動點可以支持跳小程序,而且支持在卡包列表中顯示。image

  1. 開卡表單配置。配置需要獲取到的用戶信息。 image

配置會員卡組件

  1. 在模板中添加一個會員卡組件,然后編輯中選擇模板。

image.png

  1. 選擇需要配置的模板,然后將組件上架即可。

image.png

附錄:準備支付寶應(yīng)用

  1. 登錄 支付寶開放平臺,根據(jù)需求創(chuàng)建網(wǎng)頁&移動應(yīng)用。image

  1. 添加會員卡功能image

注意:配置應(yīng)用網(wǎng)關(guān)(http(s)://web-mini的域名地址)和回調(diào)地址(http(s)://web-mini的域名地址/aliCallback)。

image

附錄:常見問題

Q:生成的領(lǐng)卡鏈接的回調(diào)地址錯誤,導(dǎo)致支付寶不能訪問,如何解決?

A:領(lǐng)卡鏈接中的回調(diào)地址的 IP 是支付寶不能訪問的地址,比如:localhost,域內(nèi) IP,導(dǎo)致領(lǐng)卡異常。系統(tǒng),訪問卡管系統(tǒng),不能用 localhost 和內(nèi)網(wǎng)地址,要用公網(wǎng)地址訪問。

Q:卡管系統(tǒng)不能訪問到業(yè)務(wù)系統(tǒng),如何解決?

A:在業(yè)務(wù)系統(tǒng)配置頁面,配置業(yè)務(wù)系統(tǒng)。

Q:會員卡領(lǐng)卡報錯 ERR010,如何解決?

A:校驗授權(quán)回調(diào)地址失敗,請檢查 callback 參數(shù)和應(yīng)用授權(quán)回調(diào)地址是否一致。進入開放平臺,將應(yīng)用的回調(diào)地址修改為 http://卡管系統(tǒng) HOST:卡關(guān)系統(tǒng) PORT/aliCallback。

image

領(lǐng)卡失敗

系統(tǒng)服務(wù)商(ISV)權(quán)限不足,建議在開發(fā)者中心檢查對應(yīng)功能是否已經(jīng)添加。 問題原因:賬號沒有卡包權(quán)限。 解決方案:添加會員分類下的權(quán)限。

imageimage.png

適用門店配置后效果

門店列表配置后再會員卡首頁最下方添加 適用門店 選項,門店列表按照 LBS 聚力顯示。

image.pngimage.png

查詢門店 ID

登錄 商家中心,進入門店管理,查看門店信息,復(fù)制門店 ID。

image.png

技術(shù)接入手冊

工作 描述 是否必須
提供用戶信息接口(含會員數(shù)據(jù)同步+查詢) 傳入用戶提交的會員信息,CRM 系統(tǒng)保存或更新會員信息,同時業(yè)務(wù)系統(tǒng)返回會員卡號和積分等會員信息。
卡狀態(tài)變更接口 支付寶會員卡新增或刪除時,將變更的會員卡信息發(fā)送給業(yè)務(wù)系統(tǒng),
獲取領(lǐng)卡鏈接 獲取支付寶領(lǐng)卡表單的鏈接地址
更新會員卡積分 調(diào)用卡管更新支付寶會員卡積分

業(yè)務(wù)系統(tǒng)提供接口給卡管系統(tǒng)(必須)

開卡信息接口

示例代碼,注意看注釋。

/**     * 開卡信息接口     * token:在管理系統(tǒng)配置的訪問業(yè)務(wù)系統(tǒng)TOKEN,可以用來防止被攻擊     *      * <p>     * HTTP,POST請求,入?yún)⒎旁贐ODY中,入?yún)⒑统鰠⒍际荕AP     * <p>     * 入?yún)⒅邪韵聟?shù):     * name(姓名)     * mobile(手機號)     * certNo(身認證)     * gender(性別)     * templateId(支付寶模板ID)     * outString(outString)     * alipayUserId(支付寶USERID, 2088開頭)     * 說明:上面的參數(shù)alipayUserId肯定存在,其他參數(shù)根據(jù)配置的領(lǐng)卡表單獲取,可能沒有值     * <p>     * 出參,要求包含以下參數(shù)     * point(當前用戶積分,必填,整數(shù),如果沒有就傳0)     * bizCardNo(業(yè)務(wù)系統(tǒng)卡號,二維碼顯示這個卡號,必填,字符串,更新積分等接口都使用業(yè)務(wù)系統(tǒng)卡號,不用保存支付寶用戶ID)     * templateId(用戶等級對應(yīng)的卡模板不是開卡鏈接中的模板時,將真正的模板ID傳回來)     * @param params     * @return     */@PostMapping("/card/openCardInfo")
public Map<String, Object> openCardInfo(@RequestHeader("token") String token, @RequestBody Map<String, Object> params ) throws Exception {
    LogUtil.info(log,"token==" + token);
    String bizCardNo = String.valueOf( params.get("bizCardNo"));
    if(StringUtils.isEmpty(bizCardNo)){
        //如果沒有傳會員卡號,走注冊邏輯        LogUtil.info(log, "走注冊邏輯");
        //TODO: 根據(jù)map中傳的身份證、手機號、姓名、支付寶UID等匹配業(yè)務(wù)系統(tǒng)的用戶,返回業(yè)務(wù)系統(tǒng)用戶ID和積分信息        String name = params.get("name")!= null ? String.valueOf(params.get("name")) : null;
        String mobile = params.get("mobile")!= null ? String.valueOf(params.get("mobile")) : null;
        String certNo = params.get("certNo")!= null ? String.valueOf(params.get("certNo")) : null;
        String gender = params.get("gender")!= null ? String.valueOf(params.get("gender")) : null;
        String templateId = String.valueOf(params.get("templateId"));
        String outString = String.valueOf(params.get("outString"));
        String alipayUserId = String.valueOf(params.get("alipayUserId"));
        name =  DESUtils.encrypt(name);
        mobile =  DESUtils.encrypt(mobile);
        certNo =  DESUtils.encrypt(certNo);
        gender =  DESUtils.encrypt(gender);
        LogUtil.info(log, "開卡信息:name:{}, mobile:{},certNo:{},gender:{},templateId:{},outString:{}",
                name, mobile, certNo, gender, templateId, outString);
        Map<String, Object> result = new HashMap<>();
        // 必填,業(yè)務(wù)系統(tǒng)用戶卡號,有兩個場景使用,參數(shù)有點不一致        result.put("bizCardNo", "xxx");
        result.put("cardNo", "xxx");
        // 非必填,用戶已有積分,如果沒有就傳0        result.put("point", "yyyy");
        // 非必填,用戶登記        result.put("level", "zzzz");
        // 非必填,用戶余額        result.put("balance", "nnnn");
        // 非必填,如果用戶等級對應(yīng)的模板和開卡對應(yīng)的模板不一致,則重新傳一個模板ID        //        result.put("templateId", "20200227000000002181302000300947");        return result;
    }else{
        //如果傳了會員卡,走會員查詢邏輯        LogUtil.info(log, "走查詢邏輯");
        // 必填,業(yè)務(wù)系統(tǒng)用戶卡號,有兩個場景使用,參數(shù)有點不一致        Map<String, Object> result = new HashMap<>();
        result.put("bizCardNo", bizCardNo);
        result.put("cardNo", bizCardNo);
        // 非必填,用戶已有積分,如果沒有就傳0        result.put("point", "yyyy");
        // 非必填,用戶登記        result.put("level", "zzzz");
        // 非必填,用戶余額        result.put("balance", "nnnn");
        return result;
    }
}

使用以下 shell 命令,測試是否能夠正常訪問。

curl -H "Content-Type:application/json" 
-XPOST http://localhost:8082/card/openCardInfo -d '{"alipayUserId":"2088"}'

卡信息變更接口

示例代碼,注意看注釋。

/** * 卡信息變更接口 * token:在管理系統(tǒng)配置的訪問業(yè)務(wù)系統(tǒng)TOKEN,可以用來防止被攻擊 * * 入?yún)⒅邪韵聟?shù): * type(變動類型): ADD\DEL * templateId(支付寶模板ID) * alipayUserId(支付寶USERID, 2088開頭):ADD、DEL類型會傳入 * bizCardNo(開卡時返回的業(yè)務(wù)系統(tǒng)卡號):ADD、DEL類型會傳入 * alipayCardNo(支付寶會員卡號):ADD、DEL類型會傳入 * * @param params * @return */@PostMapping("/card/cardChange")
public boolean cardChange(@RequestHeader("token") String token, @RequestBody 
                          Map<String, Object> params) {
    LogUtil.info(logger, "卡信息變更:token:{} ",token);
    //TODO: 業(yè)務(wù)系統(tǒng)根據(jù)自己需求    String type = String.valueOf(params.get("type"));
    String templateId = String.valueOf(params.get("templateId"));
    String alipayUserId = String.valueOf(params.get("alipayUserId"));
    String bizCardNo = String.valueOf(params.get("bizCardNo"));
    String alipayCardNo = String.valueOf(params.get("alipayCardNo"));
    LogUtil.info(logger, "卡信息變更:type:{},templateId:{},alipayUserId:{},                 bizCardNo:{},alipayCardNo:{}", type,templateId, alipayUserId,                  bizCardNo, alipayCardNo);
    return true;
}

image

商戶動態(tài)碼獲取接口

示例代碼,注意看注釋。

 /**     * 商戶動態(tài)卡碼值查詢接口     * token:在管理系統(tǒng)配置的訪問業(yè)務(wù)系統(tǒng)TOKEN,可以用來防止被攻擊     * <p>     * 入?yún)⒅邪韵聟?shù):     * appId     * templateId     * alipayUserId     *     * @param params     * @return     */    @PostMapping("/card/mdCodeChange")
    public Map<String, Object> mdCodeChange(@RequestHeader("token") String token, @RequestBody Map<String, Object> params) {
        LogUtil.info(log, "卡信息變更:token:{} , {}", token, JSON.toJSONString(params));
        String appId = String.valueOf(params.get("appId"));
        String templateId = String.valueOf(params.get("templateId"));
        String alipayUserId = String.valueOf(params.get("alipayUserId"));
        Integer expireTime = 120;
        if (params.containsKey("expireTime")) {
            expireTime = Integer.valueOf(String.valueOf(params.get("expireTime")));
        }
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("alipayUserId", alipayUserId);
        //TODO: 業(yè)務(wù)系統(tǒng)根據(jù)自己需求,生成用戶動態(tài)碼值        result.put("codeValue", System.currentTimeMillis() / 1000L + "_" + expireTime);
        //TODO: 業(yè)務(wù)系統(tǒng)根據(jù)自己需求,設(shè)置碼值過期時間, params.get("expireTime") 是卡管系統(tǒng)中配置的過期時間(秒)        result.put("codeExpire", DateUtil.format(new Date(System.currentTimeMillis() + expireTime * 1000), "yyyy-MM-dd HH:mm:ss"));
        return result;
    }

業(yè)務(wù)系統(tǒng)調(diào)用卡管系統(tǒng)(可選)

在業(yè)務(wù)代碼中更新用戶積分

/**     * 更新積分     * <p>     * 模擬調(diào)用卡管系統(tǒng)更新用戶積分     * <p>     * 真實場景應(yīng)該放在業(yè)務(wù)層中調(diào)用     *     * @param templateId 模板ID     * @param bizCardNo     業(yè)務(wù)系統(tǒng)卡號     * @param point      用戶最先積分     * @param changeReason 積分更新原因     * @return     */@PostMapping("/card/update")
public HttpResult updateCard(String templateId, String bizCardNo, String point, String changeReason)
    throws Exception {
    Map<String, String> map = new HashMap<>();
    map.put("templateId", templateId);
    map.put("bizCardNo", bizCardNo);
    map.put("point", point);
    map.put("changeReason", changeReason);
    MapUtils.removeNullValue(map);
    try {
        String signature = SignHelper.signStringParam(map, sdkConfig.getPrivateKey());
        map.put(SignHelper.SIGNATURE_PARAM_KEY, signature);
    } catch (Exception e) {
        throw new Exception("加簽失敗", e);
    }
    String result = restTemplate.postForEntity(sdkConfig.getListOfServers() + 
                                       "/card/update", map, String.class).getBody();
    Map parseResult = (Map) JSON.parse(result);
    if (parseResult.get("status").equals("OK")) {
        return HttpResult.newCorrectResult("積分更新成功");
    }
    return HttpResult.newErrorResult(parseResult.get("errmsg").toString());
}

在業(yè)務(wù)代碼中更新用戶模板

用戶登記發(fā)生變化,需要替換成不同的模板(可能包含不同的權(quán)益說明)。

/**     * 更新模板     * <p>     * 模擬調(diào)用卡管系統(tǒng)更新用戶模板     * <p>     * 真實場景應(yīng)該放在業(yè)務(wù)層中調(diào)用     *     * @param templateId 模板ID     * @param bizCardNo     業(yè)務(wù)系統(tǒng)卡號     * @param targetTemplateId    目標模板ID     * @param changeReason 積分更新原因     * @return     */    @PostMapping("/card/changeTemplate")
    public HttpResult changeTemplate(String templateId, String bizCardNo, String targetTemplateId, String changeReason)
            throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("templateId", templateId);
        map.put("bizCardNo", bizCardNo);
        map.put("targetTemplateId", targetTemplateId);
        map.put("changeReason", changeReason);
        MapUtils.removeNullValue(map);
        try {
            String signature = SignHelper.signStringParam(map, sdkConfig.getPrivateKey());
            map.put(SignHelper.SIGNATURE_PARAM_KEY, signature);
        } catch (Exception e) {
            throw new Exception("加簽失敗", e);
        }
        String result = restTemplate.postForEntity(sdkConfig.getListOfServers() + 
                                "/card/changeTemplate", map, String.class).getBody();
        Map parseResult = (Map) JSON.parse(result);
        if (parseResult.get("status").equals("OK")) {
            return HttpResult.newCorrectResult("模板更新成功");
        }
        return HttpResult.newErrorResult(parseResult.get("errmsg").toString());
    }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號