所有“卡券”类别都具有常见用例。例如,所有会员卡、礼品卡、优惠券、活动门票、登机牌和公交卡都可以通过多种方法添加到 Google Pay 应用:请选择任一方法了解详情:
通过 Android 应用
您可以使用以下方法将保存到 Google Pay 按钮添加到您的 Android 应用:
使用 Android SDK
您可以通过 Android API 将卡券保存到 Google Pay 中。您在应用中集成保存到 Google Pay 按钮后,客户就能轻松地将其卡券保存到 Google Pay。
下列步骤简要地介绍了如何添加用于添加会员卡的保存到 Google Pay 按钮,不过所有卡券的流程都相同。
1. 创建类
首先,定义 LoyaltyClass。以下示例展示了代表 LoyaltyClass 的 JSON 资源。
{
"accountIdLabel": "Member Id",
"accountNameLabel": "Member Name",
"id": "2945482443380251551.ExampleClass1",
"issuerName": "Baconrista",
"kind": "walletobjects#loyaltyClass",
"textModulesData": [
{
"header": "Rewards details",
"body": "Welcome to Baconrista rewards. Enjoy your rewards for being a loyal customer. " +
"10 points for every dollar spent. Redeem your points for free coffee, bacon and more!"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "https://maps.google.com/map?q=google",
"description": "Nearby Locations"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Call Customer Service"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "https://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Coffee beans"
}
}
}
],
"messages": [{
"header": "Welcome to Banconrista Rewards!",
"body": "Featuring our new bacon donuts.",
"kind": "walletobjects#walletObjectMessage"
}],
"locations": [{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
},{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424354,
"longitude": -122.09508869999999
},{
"kind": "walletobjects#latLongPoint",
"latitude": 37.7901435,
"longitude": -122.39026709999997
},{
"kind": "walletobjects#latLongPoint",
"latitude": 40.7406578,
"longitude": -74.00208940000002
}],
"programLogo": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "https://farm8.staticflickr.com/7340/11177041185_a61a7f2139_o.jpg"
}
},
"programName": "Baconrista Rewards",
"rewardsTier": "Gold",
"rewardsTierLabel": "Tier",
"reviewStatus": "underReview",
"hexBackgroundColor": "#ffffff",
"heroImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "https://farm8.staticflickr.com/7302/11177240353_115daa5729_o.jpg"
}
}
}2. 创建对象
创建类后,如以下代码段所示定义 LoyaltyObject:
{ "classId": "2945482443380251551.ExampleClass1", "id": "2945482443380251551.ExampleObject1", "accountId": "1234567890", "accountName": "Jane Doe", "barcode": { "alternateText": "12345", "type": "qrCode", "value": "28343E3" }, "textModulesData": [{ "header": "Jane's Baconrista Rewards", "body": "Save more at your local Mountain View store Jane. " + "You get 1 bacon fat latte for every 5 coffees purchased. " + "Also just for you, 10% off all pastries in the Mountain View store." }], "linksModuleData": { "uris": [ { "kind": "walletobjects#uri", "uri": "https://www.baconrista.com/myaccount?id=1234567890", "description": "My Baconrista Account" }] }, "infoModuleData": { "labelValueRows": [{ "columns": [{ "label": "Next Reward in", "value": "2 coffees" }, { "label": "Member Since", "value": "01/15/2013" }] }, { "columns": [{ "label": "Local Store", "value": "Mountain View" }] }], "showLastUpdateTime": "true" }, "loyaltyPoints": { "balance": { "string": "5000" }, "label": "Points", "pointsType": "points" }, "messages": [{ "header": "Jane, welcome to Banconrista Rewards!", "body": "Thanks for joining our program. Show this message to " + "our barista for your first free coffee on us!" }], "state": "active" }
3. 对未签名的 JWT 进行编码
创建对象后,将 LoyaltyClass 和 LoyaltyObject 编码到未签名的 JWT 中,如以下代码段所示:
{ "iss": "example_service_account@developer.gserviceaccount.com", "aud": "google", "typ": "savetoandroidpay", "iat": 1368029586, "payload": { "eventTicketClasses": [{ ... //Event ticket Class JSON }], "eventTicketObjects": [{ ... //Event ticket Object JSON }], "flightClasses": [{ ... //Flight Class JSON }], "flightObjects": [{ ... //Flight Object JSON }], "giftCardClasses": [{ ... //Gift card Class JSON }], "giftCardObjects": [{ ... //Gift card Object JSON }], "loyaltyClasses": [{ ... //Loyalty Class JSON }], "loyaltyObjects": [{ ... //Loyalty Object JSON }], "offerClasses": [{ ... //Offer Class JSON }], "offerObjects": [{ ... //Offer Object JSON }], "transitClasses": [{ ... //Transit Class JSON }], "transitObjects": [{ ... //Transit Object JSON }] }, "origins": ["http://baconrista.com", "https://baconrista.com"] }
4. 选择要使用的请求格式
通过 Android SDK,您可以使用以下格式之一发出请求:
savePasses方法使用 JSON 字符串载荷。savePassesJwt方法使用 JWT 字符串令牌载荷。
如需详细了解如何发出请求,请参阅调用 Android SDK。
savePasses
对 savePasses 方法的请求具有 JSON 字符串载荷。这意味着您可以直接为第 3 步中创建的对象使用 JSON。
您可以针对已存在或者在保存流程中插入的类和对象向“保存到 Google Pay”发出请求。如果卡券类别支持在同一请求中保存多个卡券,您也可以使用这一功能。您不能插入已存在的类和对象。
为了提高安全性,有些字段被视为敏感数据,在这些情况下,您无法仅通过指定对象 ID 字段来保存已存在的卡券。只有在请求中的敏感字段与已存在对象的字段匹配时,您才能保存已存在的对象。以下字段被视为敏感字段:
object.barcode.valueobject.smartTapRedemptionValue
savePassesJwt
对 savePassesJwt 方法的请求具有 JWT 字符串令牌载荷。要创建 JWT,请使用您的 OAuth 2.0 服务帐号私钥对第 3 步中的对象进行签名。下列代码段展示了如何使用各种语言来编码 JWT。
Java
WobCredentials credentials = null; WobUtils utils = null; // Instantiate the WobUtils class which contains handy functions // Wob utils can be found in the quickstart sample try { credentials = new WobCredentials( ServiceAccountEmailAddress, ServiceAccountPrivateKeyPath, ApplicationName, IssuerId); utils = new WobUtils(credentials); } catch (GeneralSecurityException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // Add valid domains for the Save to Wallet button List<String> origins = new ArrayList<String>(); origins.add("http://baconrista.com"); origins.add("https://baconrista.com"); origins.add(req.getScheme() + "://" + req.getServerName() + ":" + req.getLocalPort()); //Generate Objects and Classes here //........ WobPayload payload = new WobPayload(); payload.addObject({WalletObject/WalletClass}); // Convert the object into a Save to Android Pay Jwt String jwt = null; try { jwt = utils.generateSaveJwt(payload, origins); } catch (SignatureException e) { e.printStackTrace(); }
PHP
$requestBody = [ "iss"=> SERVICE_ACCOUNT_EMAIL_ADDRESS, "aud" => "google", "typ" => "savetoandroidpay", "iat"=> time(), "payload" => { "eventTicketClasses" => [ ], # Event ticket classes "eventTicketObjects" => [ ], # Event ticket objects "flightClasses" => [ ], # Flight classes "flightObjects" => [ ], # Flight objects "giftCardClasses" => [ ], # Gift card classes "giftCardObjects" => [ ], # Gift card objects "loyaltyClasses" => [ ], # Loyalty classes "loyaltyObjects" => [ ], # Loyalty objects "offerClasses" => [ ], # Offer classes "offerObjects" => [ ], # Offer objects "transitClasses" => [ ], # Transit classes "transitObjects" => [ ] # Transit objects }, "origins" => ["http://baconrista.com", "https://baconrista.com"] ] // Generate the Save to Android Pay Jwt echo $jwt = $assertObj->makeSignedJwt($requestBody, $client);
Python
jwt = { 'iss': config.SERVICE_ACCOUNT_EMAIL_ADDRESS, 'aud': 'google', 'typ': 'savetoandroidpay', 'iat': int(time.time()), 'payload': { 'webserviceResponse': { 'result': 'approved', 'message': 'Success.' }, 'eventTicketClasses': [], # Event ticket classes 'eventTicketObjects': [], # Event ticket objects 'flightClasses': [], # Flight classes 'flightObjects': [], # Flight objects 'giftCardClasses': [], # Gift card classes 'giftCardObjects': [], # Gift card objects 'loyaltyClasses': [], # Loyalty classes 'loyaltyObjects': [], # Loyalty objects 'offerClasses': [], # Offer classes 'offerObjects': [], # Offer objects 'transitClasses': [], # Transit classes 'transitObjects': [] # Transit objects }, 'origins' : ['http://baconrista.com', 'https://baconrista.com'] } // Generate the Save to Android Pay Jwt signer = crypt.Signer.from_string(app_key) signed_jwt = crypt.make_signed_jwt(signer, jwt) response = webapp2.Response(signed_jwt)
您可以针对已存在或者在保存流程中插入的类和对象向“保存到 Google Pay”发出请求。如果卡券类别支持在同一请求中保存多个卡券,您也可以使用这一功能。您不能插入已存在的类和对象。只要类和对象已经存在,就可以使用瘦 JWT。
5. 调用 Android SDK
首先,使用 getPayApiAvailabilityStatus 方法检查 savePasses 或 savePassesJwt 方法是否可用,如以下示例所示:
import com.google.android.gms.common.api.UnsupportedApiCallException; import com.google.android.gms.pay.Pay; import com.google.android.gms.pay.PayApiAvailabilityStatus; import com.google.android.gms.pay.PayClient; … PayClient payClient = Pay.getClient(this); payClient // Use PayClient.RequestType.SAVE_PASSES_JWT for the savePassesJwt API .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES) .addOnSuccessListener( status -> { switch (status) { case PayApiAvailabilityStatus.AVAILABLE: // You can call the savePasses API or savePassesJwt API ... break; case PayApiAvailabilityStatus.NOT_ELIGIBLE: default: // We recommend to either: // 1) Hide the save button // 2) Fall back to a different Save Passes integration (e.g. JWT link) // Note however that the user *will* only be able to access their // passes on web // A not eligible user might become eligible in the future. ... break; } }) .addOnFailureListener( exception -> { if (exception instanceof UnsupportedApiCallException) { // Google Play Services too old. We could not check API availability or // user eligibility. We recommend to either: // 1) Fall back to a different Save Passes integration (e.g. JWT link) // Note however that the user *may* only be able to access their // passes on web // 2) Hide the save button ... } else { // Very old version of Google Play Services or unexpected error! ... } });
如果该 API 可用,则在用户点按保存到 Google Pay 按钮时调用 savePasses 或 savePassesJwt 方法。
savePasses
private static final int SAVE_TO_GOOGLE_PAY = 1000; … String jsonString = … // Build or fetch JSON request PayClient payClient = Pay.getClient(this); payClient.savePasses(jsonString, this, SAVE_TO_GOOGLE_PAY);
savePassesJwt
private static final int SAVE_TO_GOOGLE_PAY = 1000; … String jwtString = … // Fetch JWT from a secure server PayClient payClient = Pay.getClient(this); payClient.savePassesJwt(jwtString, this, SAVE_TO_GOOGLE_PAY);
此调用会触发保存流程。流程完成后,您的应用会使用 onActivityResult 解析结果。在您的 Activity 中,需要按如下方式定义此接收器:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // `data` will only have information in the `SAVE_ERROR` case if (requestCode == SAVE_TO_GOOGLE_PAY) { switch (resultCode) { case Activity.RESULT_OK: // Save successful ... break; case Activity.RESULT_CANCELED: // Save canceled ... break; case PayClient.SavePassesResult.API_ERROR: // API error - this should not happen if getPayApiAvailabilityStatus is // used correctly ... break; case PayClient.SavePassesResult.SAVE_ERROR: // Save error - check EXTRA_API_ERROR_MESSAGE to debug the issue // Most save errors indicate an error in the app fingerprint or the Json // request payload. In most cases prompting the user to try again will not // help. if (data != null && !isEmpty(data.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE))) { ... } else { // Unexpected! A save error should always have a debug message associated // with it ... } break; case PayClient.SavePassesResult.INTERNAL_ERROR: default: // Internal error - prompt the user to try again, if the error persists // disable the button ... break; } } else { ... } }
6. 将“保存到 Google Pay”按钮添加到界面中
Google Pay 提供了供您集成到应用中的 Android SDK 按钮。按钮素材资源可以在品牌推广指南中找到。
此工具包包含各种按钮的矢量图。
要将按钮集成到应用中,请将工具包中的按钮图片复制到应用的 res 文件夹,并向您的 Android 布局文件添加以下代码。请注意,除了要有正确的 src 值,每个按钮都要有唯一的 contentDescription 字符串和 minWidth 值。
<ImageButton android:layout_width="match_parent" android:layout_height="48dp" android:minWidth="200dp" android:clickable="true" android:src="@drawable/s2ap" />
按钮的 layout_height 是 48dp,minWidth 必须是 200dp。
使用 JWT 链接和 Intent 方法
要从应用将卡券保存到 Google Pay,请按以下步骤操作:
- 完成将“保存到 Google Pay”按钮添加到电子邮件或短信中的步骤。
使用
ACTION_VIEWIntent 打开保存到 Google Pay 按钮包含的深层链接。请确保触发 Intent 的按钮遵循品牌推广指南。
下面是流程摘要示例:
- 在保存卡券之前的某个时间,使用 REST API 在后端创建一个类。
- 当最终用户要求保存卡券时,您的服务器后端会将
JWT发送给代表对象的 Android 客户端应用。 - 您的 Android 客户端应用包含符合我们品牌推广指南的保存到 Google Pay 按钮。
点击该按钮后,将针对路径中含有 JWT 的 URI 打开一个
ACTION_VIEWIntent。示例如下:https://pay.google.com/gp/v/save/{jwt_generated}
使用 JWT POST 请求方法
JWT POST 请求方法是为 Android 应用创建机票或活动门票类和对象的替代方法。在保存对象之前难以实现创建和插入类所需的后端工作时,就会使用此方法。此方法对于活动门票和登机牌非常有用,随着时间的推移,这些卡券会创建许多类。其流程归纳如下:
- 当最终用户办理登机手续或兑换活动门票时,您的服务器后端会将一个 JWT 呈现给同时包含该类和对象的 Android 客户端应用。
- 您的 Android 客户端应用包含符合我们品牌推广指南的保存到 Google 按钮。 点击该按钮时,将发生以下情况:
POST请求通过 HTTPS 将 JWT 发送到 Google 端点。- 作为回应,系统将发送所产生 HTTP 响应正文中的 URI,该 URI 随后应该用于打开
ACTION_VIEWIntent。
JWT POST 请求方法还需要一个 API 密钥,它将以查询参数的形式附加到 REST API 调用。
类创建
只有在使用过去未保存的 class.id 呈现时,才会在后端创建新类。因此,虽然您可能会多次通过 JWT 将类详细信息传递给 Google,但后端会识别出该类已保存,并且不会在每次保存登机牌时都创建新类。
类更新
在第一张登机牌后,对象与类一起保存。您可以按照预期将 class.id 与我们的 REST API 一起使用,以便按预期执行 ADDMESSAGE、GET、LIST、PATCH 和 UPDATE 操作。
要更改类详细信息,您必须使用 Class Update API。如果您使用 class.id=XYZ 以及其他一些类详细信息来创建类,并且稍后尝试使用 class.id=XYZ 和不同的类详细信息来创建类,我们仍会保留原始类,不应用任何更改。
JWT 格式
如需详细了解您所发送的 JWT 的格式,请参阅我们的 Google Pay API for Passes JWT 参考文档。对于此 payload,您为对象传递一个条目(表示要创建的对象),并为类传递一个条目(其中包含您所创建的类)。
HTTP 请求
您可以使用 INSERT 方法插入 JWT 中指定的类和对象,您必须将 API 密钥设置为查询参数。
JWT INSERT 方法
对于 JWT,INSERT 方法将插入 JWT 中指定的类和对象。如果成功,它将返回一个 200 HTTP 响应。
HTTP 请求
POST https://walletobjects.googleapis.com/walletobjects/v1/jwt/
授权
此请求无需授权。但是,JWT 必须使用 RSA-SHA256 签名。签名密钥是 OAuth 服务帐号生成的密钥。
请求正文
在请求正文中,请提供具有以下结构的数据:
{ “jwt” : string }响应正文
如果成功,此方法将返回具有以下结构的响应正文:
{
"saveUri": string,
"resources": {
"eventTicketClasses": [ eventTicketClass resource, ... ],
"eventTicketObjects": [ eventTicketObject resource, ... ],
"flightClasses": [ flightClass resource, ... ],
"flightObjects": [ flightObject resource, ... ],
"giftCardClasses": [ giftCardClass resource, ... ],
"giftCardObjects": [ giftCardObject resource, ... ],
"loyaltyClasses": [ loyaltyClass resource, ... ],
"loyaltyObjects": [ loyaltyObject resource, ... ],
"offerClasses": [ offerClass resource, ... ],
"offerObjects": [ offerObject resource, ... ],
"transitClasses": [ transitClass resource, ... ],
"transitObjects": [ transitObject resource, ... ]
}
}saveUri 是一个 URI,打开此 URI 后,最终用户可以将 JWT 中标识的对象保存到他们的 Google 帐号。此 URI 仅在返回后的一周内有效。
如需了解详情,请参阅 JWT 端点参考。
流程图
如需了解流程图,请参阅典型的 API 流程。