每张卡券的内容分为两个部分,商户固定信息(模板)及用户特有信息(实例)。
模板:用户共有的字段信息,所有用户将共同享有模板信息字段,如logo、merchantName等。
用户特有信息(实例):用户特有的字段信息,如cardNumber。如果实例和模板中有相同字段则实例信息将覆盖模板信息内容。
用户点击按钮时,打开Webview页面,并通过Intent将JWE发送给华为服务器,获取添加结果并展示给用户。

具体步骤如下:
定义包含商户信息的模板信息,并通过REST API推送到华为服务器
在应用中自定义按钮
用户点击按钮时,生成卡券对应的对象信息,以JWT形式作为参数发送到华为服务器
抽取卡券信息中通用的商户信息作为模板。
- HwWalletObject hwWalletObject = new HwWalletObject();
- hwWalletObject.setFormatVersion("10.0");
- hwWalletObject.setPassStyleIdentifier("Loyalty001"); // 模板标识,唯一值
- hwWalletObject.setPassTypeIdentifier("hwpass.loyalty.merchant"); // 注册时的服务号
- hwWalletObject.setWebServiceURL("https://www.xxx.com"); // 用户领取卡券时的回调地址
- Fields fields = new Fields();
- fields.setCountryCode("CN");
- // 地理围栏信息
- List<Location> locationList = new ArrayList<>();
- Location location = new Location();
- location.setLongitude("11x.0679603815");
- location.setLatitude("2x.6592051284");
- locationList.add(location);
- fields.setLocationList(locationList);
- List<CommonFields> commonFields = new ArrayList<>();
- CommonFields cardImage = new CommonFields();
- cardImage.setKey("backgroundImage");
- cardImage.setValue("https://www.xxx.com/.../cardImage.png"); // 商户会员卡卡面图片URI
- commonFields.add(cardImage);
- CommonFields logo = new CommonFields();
- logo.setKey("logo");
- logo.setValue("https://www.xxx.com/.../logo.png"); // 商户LOGO URI
- commonFields.add(logo);
- // 商户名称,含多语言样例,例如此处设置字符串id为merchantNameI18N,用于在Localized中获取对应语言取值进行展示
- CommonFields merchantName = new CommonFields();
- merchantName.setKey("merchantName");
- merchantName.setValue("Some merchant name");
- merchantName.setLocalizedValue("merchantNameI18N");
- commonFields.add(merchantName);
- // 卡片名称
- CommonFields name = new CommonFields();
- name.setKey("name");
- name.setValue("Loyalty Card");
- commonFields.add(name);
- fields.setCommonFields(commonFields);
- List<AppendFields> appendFields = new ArrayList<>();
- AppendFields nearbyLocations = new AppendFields();
- nearbyLocations.setKey("nearbyLocations");
- nearbyLocations.setValue("https://www.xxx.com/.../nearbyLocation.html"); // 附近门店地址URI
- nearbyLocations.setLabel("Nearby Locations");
- appendFields.add(nearbyLocations);
- AppendFields website = new AppendFields();
- website.setKey("website");
- website.setValue("https://www.xxx.com/.../merchantMainPage.html"); // 商户主页URI
- website.setLabel("Website");
- appendFields.add(website);
- AppendFields hotline = new AppendFields();
- hotline.setKey("hotline");
- hotline.setValue("400820XXXX"); // 商户热线
- hotline.setLabel("Hotline");
- appendFields.add(hotline);
- fields.setAppendFields(appendFields);
- List<ValueObject> imageList = new ArrayList<>();
- ValueObject imagePic = new ValueObject();
- imagePic.setValue("https://www.xxx.com/.../banner.png"); // 推广图片
- imageList.add(imagePic);
- fields.setImageList(imageList);
- List<Localized> localizedList = new ArrayList<>();
- // 商户中文名称,例如中文语言时,通过上面指定的字符串Id merchantNameI18N匹配到此Value用于展示
- Localized merchantNameChinese = new Localized();
- merchantNameChinese.setKey("merchantNameI18N");
- merchantNameChinese.setLanguage("zh-CN");
- merchantNameChinese.setValue("Merchant name in Chinese");
- localizedList.add(merchantNameChinese);
- // 商户英文名称,例如英文语言时,通过上面指定的字符串Id merchantNameI18N匹配到此Value用于展示
- Localized merchantNameEnglish = new Localized();
- merchantNameEnglish.setKey("merchantNameI18N");
- merchantNameEnglish.setLanguage("en-GB");
- merchantNameEnglish.setValue("Merchant name in English");
- localizedList.add(merchantNameEnglish);
- fields.setLocalized(localizedList);
- hwWalletObject.setFields(fields);
通过如下POST接口推送到华为服务器。
- POST
- https://{walletkit_server_url}/hmspass/v1/loyalty/model
备注:请求地址中的{walletkit_server_url}变量需要开发者根据服务器所属区域自行选择,参考钱包服务器地址列表。
China | passentrust-drcn.wallet.hicloud.com |
Russia | passentrust-drru.wallet.hicloud.com |
Asia, Africa, and Latin America | passentrust-dra.wallet.hicloud.com |
Europe | passentrust-dre.wallet.hicloud.com |
推送hwWalletObject代码如下:
- boolean validateClass = HwWalletObjectUtil.validateHwWalletObjectClass(hwWalletObject);
- if (validateClass) {
- // post hwWalletObject to Huawei server
- WalletBuildService walletBuildService = new WalletBuildServiceImpl();
- HwWalletObject loyaltyClass = walletBuildService.postHwWalletClassToWalletServer("loyalty/model", hwWalletObject);
- }
在应用中自定义按钮,并将如下内容作为链接:
- hms://www.huawei.com/payapp/{jwt_content}
用户在点击按钮时,生成包含用户特有信息的对象。
- HwWalletObject hwWalletObject = new HwWalletObject();
- hwWalletObject.setSerialNumber("854687156"); // 对象唯一标识,在模板下唯一
- Fields fields = new Fields();
- // 状态,生效卡片才能添加
- Status status = new Status();
- status.setState("active");
- status.setEffectTime("2019-11-11T00:00:00.000Z");
- status.setExpireTime("2029-11-10T23:59:59.999Z");
- fields.setStatus(status);
- // 关联优惠券,填入关联优惠券的模板和对象标识
- List<LinkedPassId> relatedPassIds = new ArrayList<>();
- LinkedPassId linkedCoupon = new LinkedPassId();
- linkedCoupon.setTypeId("Coupon template id");
- linkedCoupon.setId("Coupon instance id");
- relatedPassIds.add(linkedCoupon);
- fields.setRelatedPassIds(relatedPassIdss);
- // 条形码/二维码
- BarCode barCode = new BarCode();
- barCode.setText("Refresh every ten minutes");
- barCode.setType("codabar"); // codabar or qrCode
- barCode.setValue("829383819844342342");
- fields.setBarCode(barCode);
- List<CommonFields> commonFields = new ArrayList<>();
- // 会员名称,含多语言样例
- CommonFields accountName = new CommonFields();
- accountName.setKey("accountName");
- accountName.setValue("Member name");
- accountName.setLocalizedValue("accountName18N");
- commonFields.add(accountName);
- // 卡号
- CommonFields cardNumber = new CommonFields();
- cardNumber.setKey("cardNumber");
- cardNumber.setValue("4527809736");
- commonFields.add(cardNumber);
- fields.setCommonFields(commonFields);
- List<AppendFields> appendFields = new ArrayList<>();
- // 会员积分
- AppendFields points = new AppendFields();
- points.setKey("points");
- points.setLabel("Points");
- points.setValue("1299");
- appendFields.add(points);
- AppendFields balance = new AppendFields();
- balance.setKey("balance");
- balance.setValue("$19");
- balance.setLabel("Balance");
- appendFields.add(balance);
- // 等级
- AppendFields level = new AppendFields();
- level.setKey("level");
- level.setValue("Gloden");
- level.setLabel("Level");
- appendFields.add(level);
- fields.setAppendFields(appendFields);
- List<Localized> localizedList = new ArrayList<>();
- // 会员中文名称
- Localized accountNameChinese = new Localized();
- accountNameChinese.setKey("accountName18N");
- accountNameChinese.setLanguage("zh-CN");
- accountNameChinese.setValue("Member name in Chinese");
- localizedList.add(accountNameChinese);
- // 会员英文名称
- Localized accountNameEnglish = new Localized();
- accountNameEnglish.setKey("accountName18N");
- accountNameEnglish.setLanguage("en-GB");
- accountNameEnglish.setValue("Member name in English");
- localizedList.add(accountNameEnglish);
- fields.setImageList(localizedList);
- hwWalletObject.setFields(fields);
采用JWE格式生成数据,总共分为5段,sessionKey和IV部分建议每次动态变换。。
第一段为Head部分;
第二段为采用RSA加密的sessionKey数据,华为服务器已经开放了RSA公钥;
第三段为长度12位的IV部分;
第四段为加密并压缩后的HwWalletObject字符串,采用第二段的sessionKey作为对称秘钥和第三段的IV作为加密向量,以AES-GCM方式对HwWalletObject字符串进行加密并压缩;
第五段为签名部分,将前面4部分使用JWE的私钥(开发者联盟申请得到)进行签名,生成签名字符串。每一段均为Base64编码。
以下代码中引用但未实现的类(“CommonUtil”,“RandomUtils”,“JweUtil”)均可以在服务器示例代码中查看具体定义。
- // 在华为AppGallery Connect网站上注册的应用ID。
- String appId = "Replace with your app ID";
- //生成一个新的实例并将其绑定到用户,示例代码中使用直接读取json文件的形式来设置Instance,商户接入时建议使用定义HwWalletObject形式来生成JWE数据
- //HwWalletObject可以在服务器示例代码中查看具体定义。
- //String newInstanceJson = CommonUtil.readWalletObject("LoyaltyInstance.json");
- String newInstanceJson = CommonUtil.toJson(hwWalletObject);
- JSONObject newInstanceJsonObject = JSONObject.parseObject(newInstanceJson);
- newInstanceJsonObject.put("iss", appId);
- //生成会话密钥以加密有效载荷数据。 会话密钥是一串随机的十六进制数字。
- String sessionKey = RandomUtils.generateSecureRandomFactor(16);
- System.out.println("sessionKey: " + sessionKey);
- //华为固定的公开密钥,用于加密会话密钥。
- String sessionKeyPublicKey = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAgBJB4usbO33Xg5vhJqfHJsMZj44f7rxpjRuPhGy37bUBjSLXN+dS6HpxnZwSVJCtmiydjl3Inq3Mzu4SCGxfb9RIjqRRfHA7ab5p3JnJVQfTEHMHy8XcABl6EPYIJMh26kztPOKU2Mkn6yhRaCurhVUD3n9bD8omiNrR4rg442AJlNamA7vgKs65AoqBuU4NBkGHg0VWWpEHCUx/xyX6hIwqc1aD7P2f62ZHsKpNZBOek/riWhaVx3dTAa9ZS+Av3IGLOZiplhYIow9f8dlWyqs8nff9FZoJO03QhXLvOORT+lPAkW6gFzaoeMaGb40HakkZn3uvlAEKrKrtR0rZEok+N1hnboaAu8oaKK0rF1W6iNrXcFrO0rcrCsFTVF8qCa/1dFmIXwUd2M6cUzT9W0YkNyb6ZBbwEhjwBL4DNW4JfeF2Dzj0eZYlSuYV7e7e1e+XEO8lwPLAiy4bEFAWCaeuDVIhbIoBaU6xHNVQoyzct98gaOYxE4mVDqAUVmhfAgMBAAE=";
- //您在AGC上申请服务时生成了一对密钥。在此使用该私钥。
- String jweSignPrivateKey = "Replace with your private key.";
- //生成JWE
- String jweStrByInstanceIds = JweUtil.generateJwe(sessionKey, jweSignPrivateKey,newInstanceJsonObject.toJSONString(), sessionKeyPublicKey);
通过Intent方式将对象JWT发到华为服务器
注:JWE数据需要经过URLEncoder.encode(jweStrByInstanceIds, StandardCharsets.UTF_8.toString())编码。
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("hms://www.huawei.com/payapp/{" + jwt_content + "}"));
- try {
- startActivityForResult(intent, requestCode)
- } catch (ActivityNotFoundException e) {
- …
- }