ขั้นตอนการพัฒนาบัตร Google Wallet

Google Wallet API มีชุดประเภทบัตรที่กำหนดไว้ล่วงหน้าและปรับให้เหมาะกับกรณีการใช้งานเฉพาะ เช่น บัตรของขวัญ บอร์ดดิ้งพาส ตั๋วเข้างาน และอื่นๆ นอกจากนี้ ยังมีประเภทบัตรทั่วไปสำหรับกรณีการใช้งานที่ไม่มีประเภทบัตรที่เฉพาะเจาะจงด้วย

บทความนี้จัดทำขึ้นเพื่อทำความคุ้นเคยกับขั้นตอนพื้นฐานที่จำเป็นในการสร้างและออกบัตรโดยใช้ Google Wallet API การดำเนินการบางขั้นตอนที่อธิบายไว้ด้านล่างมีหลายวิธี แต่ในระดับสูง บัตรทุกประเภทจะสร้างขึ้นด้วยขั้นตอนการพัฒนาพื้นฐานเดียวกัน

สำหรับคำแนะนำโดยละเอียดเกี่ยวกับการสร้างบัตร โปรดดูคำแนะนำสำหรับเว็บ อีเมล และ SMS หรือแอป Android

มีไว้เพื่ออะไร

Passes Class กำหนดชุดพร็อพเพอร์ตี้ที่ใช้ร่วมกันในบัตรหลายใบ คล้ายกับเทมเพลต ตัวอย่างเช่น หากคุณออกตั๋วกิจกรรม ระดับ Passes จะกำหนดช่องที่เหมือนกันในตั๋วทั้งหมด เช่น ชื่อกิจกรรม วันที่ และเวลา

บัตรทุกใบที่คุณออกต้องอ้างอิงคลาส Passes นอกจากนี้ คุณต้องกำหนดรหัสที่ไม่ซ้ำกันให้กับบัตรทุกคลาสที่คุณสร้าง ซึ่งใช้อ้างอิงรหัสเมื่อสร้างบัตร

การทำงาน

Passes Class จะอยู่ในรูปแบบ JSON และสร้างขึ้นได้ด้วย Google Wallet REST API, Android SDK หรือใน Google Wallet Business Console

แสดงตัวอย่างคลาสบัตร

{
  "id": "ISSUER_ID.EVENT_CLASS_ID",
  "issuerName": "[TEST ONLY] Heraldic Event",
  "localizedIssuerName": {
    "defaultValue": {
      "language": "en-US",
      "value": "[TEST ONLY] Heraldic Event"
    }
  },
  "logo": {
    "sourceUri": {
      "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "LOGO_IMAGE_DESCRIPTION"
      }
    }
  },
  "eventName": {
    "defaultValue": {
      "language": "en-US",
      "value": "Google Live"
    }
  },
  "venue": {
    "name": {
      "defaultValue": {
        "language": "en-US",
        "value": "Shoreline Amphitheater"
      }
    },
    "address": {
      "defaultValue": {
        "language": "en-US",
        "value": "ADDRESS_OF_THE_VENUE"
      }
    }
  },
  "dateTime": {
    "start": "2023-04-12T11:30"
  },
  "reviewStatus": "UNDER_REVIEW",
  "hexBackgroundColor": "#264750",
  "heroImage": {
    "sourceUri": {
      "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "HERO_IMAGE_DESCRIPTION"
      }
    }
  }
}
    

มีไว้เพื่ออะไร

ออบเจ็กต์ Passes กำหนดพร็อพเพอร์ตี้ของบัตรที่ไม่ซ้ำกันที่จะออกให้กับผู้ใช้ที่เจาะจง เช่น ออบเจ็กต์บัตรสำหรับตั๋วเข้างานจะกำหนดช่องที่ไม่ซ้ำกันสำหรับตั๋วแต่ละใบ เช่น หมายเลขที่นั่งหรือคิวอาร์โค้ดสำหรับตั๋วใบนั้น

เมื่อสร้างออบเจ็กต์ Passes แล้ว Google Wallet API จะจัดเก็บบัตรใหม่และเชื่อมโยงบัตรดังกล่าวกับบัญชีผู้ออกบัตร บัตรที่จัดเก็บไว้นี้เป็นการรวมกันของพร็อพเพอร์ตี้ที่ไม่ซ้ำกันของออบเจ็กต์บัตรและพร็อพเพอร์ตี้เทมเพลตของคลาสบัตรที่เกี่ยวข้อง

รวมถึงต้องกำหนดรหัสที่ไม่ซ้ำกันให้กับออบเจ็กต์ของ Passes แต่ละรายการ เพื่อใช้ในการอ้างอิงเมื่อออกบัตร

การทำงาน

ออบเจ็กต์ Passes ได้รับการกำหนดรูปแบบ JSON และสร้างขึ้นด้วย Google Wallet REST API หรือ Android SDK ได้

แสดงตัวอย่างออบเจ็กต์ Passes

{
  "id": "ISSUER_ID.OBJECT_ID",
  "classId": "ISSUER_ID.EVENT_CLASS_ID",
  "state": "ACTIVE",
  "seatInfo": {
    "seat": {
      "defaultValue": {
        "language": "en-us",
        "value": "5"
      }
    },
    "row": {
      "defaultValue": {
        "language": "en-us",
        "value": "G"
      }
    },
    "section": {
      "defaultValue": {
        "language": "en-us",
        "value": "40"
      }
    },
    "gate": {
      "defaultValue": {
        "language": "en-us",
        "value": "3A"
      }
    }
  },
  "barcode": {
    "type": "QR_CODE",
    "value": "BARCODE_VALUE",
    "alternateText": ""
  }
}
    

มีไว้เพื่ออะไร

หากต้องการออกบัตรผ่านให้กับผู้ใช้ จะต้องเข้ารหัสคลาส Passes และออบเจ็กต์ Passes ใน JSON Web Token (JWT) รูปแบบ JWT เป็นมาตรฐานร่วมและเปิดกว้างสำหรับการเป็นตัวแทนการอ้างสิทธิ์ระหว่าง 2 ฝ่าย ในกรณีที่ออกบัตรด้วย Google Wallet API ระบบจะใช้ JWT เพื่อส่งการอ้างสิทธิ์ว่าผู้ใช้มีสิทธิเข้าถึงบัตรบางใบที่เชื่อมโยงกับบัญชีผู้ออกบัตร

เมื่อมีการส่ง JWT ไปยัง Google Wallet API ระบบจะใช้ข้อมูลที่เข้ารหัสเพื่อระบุบัตรที่เฉพาะเจาะจงและออกบัตรให้กับผู้ใช้ และหากมีการออกบัตรแล้ว ข้อมูลนี้ยังช่วยให้ Google Wallet API ระบุได้ว่าบัตรนั้นซ้ำกัน เพื่อไม่ให้มีการเพิ่มบัตรลงใน Google Wallet ของผู้ใช้มากกว่า 1 ครั้ง

การทำงาน

JWT จะกำหนดเป็นรูปแบบ JSON ตามข้อมูลจำเพาะของ JWT หากต้องการกำหนด JWT สำหรับการออกบัตรด้วย Google Wallet API คุณต้องระบุข้อมูลเกี่ยวกับบัตรที่ต้องการออกในพร็อพเพอร์ตี้ payload ของ JWT

แสดงตัวอย่าง JWT

{
  "iss": "issuer@example.com",
  "aud": "google",
  "typ": "savetowallet",
  "iat": 1696877738,
  "origins": [
    "www.example.com"
  ],
  "payload": {
    "eventTicketObjects": [
      {
        "id": "ISSUER_ID.LOYALTY_OBJECT_SUFFIX"
      }
    ]
  }
}
    

มีไว้เพื่ออะไร

JWT ทั้งหมดที่ส่งไปยัง Google Wallet API เพื่อออกบัตรจะต้องมีการลงนามด้วยข้อมูลรับรองที่คุณให้ไว้ก่อนหน้านี้ใน Google Wallet Business Console การลงนามจะใช้ข้อมูลเข้าสู่ระบบของคุณในการเข้ารหัส JWT เพื่อให้บัตรปลอดภัยอยู่เสมอ และเพื่ออนุญาตให้ Google Wallet API ตรวจสอบสิทธิ์ว่ารายละเอียดบัตรที่เข้ารหัสในบัตรนั้นถูกต้องและเชื่อมโยงกับบัญชีผู้ออกบัตร

การทำงาน

ไลบรารีของไคลเอ็นต์ Google Wallet และ Android SDK มอบวิธีที่สะดวกในการลงชื่อ JWT ของคุณ นอกจากนี้ยังมีไลบรารีโอเพนซอร์สจำนวนมากที่ช่วยจัดการความซับซ้อนในการเซ็นโค้ดให้คุณได้เลือกใช้

สำหรับผู้ที่ใช้ Google Wallet REST API ในการออกบัตร JWT จะลงนามด้วยคีย์บัญชีบริการ Google Cloud สำหรับผู้ที่ใช้ Google Wallet Android SDK ทาง SDK จะจัดการการลงนาม JWT ด้วยลายนิ้วมือ SHA-1 ของใบรับรอง App Signing โดยอัตโนมัติ

เพื่อปกป้องข้อมูลรับรองของคุณ JWT ควรลงชื่อเฉพาะบนเซิร์ฟเวอร์ของคุณหรือใช้ Google Wallet Android SDK ในแอปของคุณ

แสดงตัวอย่างการรับรองโค้ด

Java

  // Create the JWT as a HashMap object
  HashMap claims = new HashMap();
  claims.put("iss", ((ServiceAccountCredentials) credentials).getClientEmail());
  claims.put("aud", "google");
  claims.put("origins", Arrays.asList("www.example.com"));
  claims.put("typ", "savetowallet");

  // Create the Google Wallet payload and add to the JWT
  HashMap payload = new HashMap();
  payload.put("eventTicketObjects", Arrays.asList(newObject));
  claims.put("payload", payload);

  // Google Cloud service account credentials are used to sign the JWT
  Algorithm algorithm =
      Algorithm.RSA256(
          null, (RSAPrivateKey) ((ServiceAccountCredentials) credentials).getPrivateKey());
  String token = JWT.create().withPayload(claims).sign(algorithm);
        

Node.JS

  // Create the JWT claims
  let claims = {
    iss: this.credentials.client_email,
    aud: 'google',
    origins: ['www.example.com'],
    typ: 'savetowallet',
    payload: {
      eventTicketObjects: [newObject]
    },
  };

  // The service account credentials are used to sign the JWT
  let token = jwt.sign(claims, this.credentials.private_key, { algorithm: 'RS256' });
        

Python

  # Create the JWT claims
  claims = {
      'iss': self.credentials.service_account_email,
      'aud': 'google',
      'origins': ['www.example.com'],
      'typ': 'savetowallet',
      'payload': {
          # The listed classes and objects will be created
          'eventTicketObjects': [new_object]
      }
  }

  # The service account credentials are used to sign the JWT
  signer = crypt.RSASigner.from_service_account_file(self.key_file_path)
  token = jwt.encode(signer, claims).decode('utf-8')
        

มีไว้เพื่ออะไร

เมื่อคุณสร้าง JWT ที่ลงชื่อแล้ว คุณก็พร้อมที่จะออกบัตรให้กับผู้ใช้ Google Wallet! ซึ่งทำได้โดยการนำเสนอปุ่มหรือลิงก์ "เพิ่มลงใน Google Wallet" ให้แก่ผู้ใช้ เมื่อผู้ใช้คลิกปุ่มหรือไฮเปอร์ลิงก์ ระบบจะส่ง JWT ที่ลงชื่อไปยัง Google Wallet API ซึ่งจะถอดรหัสโดยใช้ข้อมูลเข้าสู่ระบบที่บันทึกไว้ เมื่อลายเซ็น JWT ผ่านการตรวจสอบสิทธิ์แล้ว ระบบจะออกบัตรให้ผู้ใช้เพื่อบันทึกใน Google Wallet

การทำงาน

ในการสร้างปุ่ม "เพิ่มลงใน Google Wallet" สำหรับแอป Android ให้ใช้ Google Wallet Android SDK ซึ่งนำเสนอวิธีสร้างปุ่มดังกล่าว สำหรับแพลตฟอร์มอื่นๆ ทั้งหมด ซึ่งรวมถึงเว็บ อีเมล และ SMS ให้สร้างไฮเปอร์ลิงก์ในรูปแบบ https://pay.google.com/gp/v/save/<signed_jwt> หากเป็นไปได้ ขอแนะนำให้คุณส่งลิงก์นี้ไปให้ผู้ใช้เป็นปุ่ม "เพิ่มลงใน Google Wallet"

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการใช้ปุ่ม "เพิ่มลงใน Google Wallet" โปรดดูหลักเกณฑ์การใช้แบรนด์ของ Google Wallet API

แสดงตัวอย่างโค้ด

  https://pay.google.com/gp/v/save/<signed_jwt>
        

Android SDK

  private lateinit var walletClient: PayClient
  private val addToGoogleWalletRequestCode = 1000
  private lateinit var addToGoogleWalletButton: View

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    walletClient = Pay.getClient(this)
    addToGoogleWalletButton.setOnClickListener {
      walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode)
    }
  }
        

เมื่อผู้ใช้บันทึกแล้ว บัตรดังกล่าวจะปรากฏในแอป Google Wallet พร้อมกับบัตรอื่นๆ ที่บันทึกไว้

การสร้างออบเจ็กต์และคลาสบัตรใน JWT

คุณสามารถสร้างบัตรประเภทบัตรและออบเจ็กต์บัตรล่วงหน้าได้โดยใช้ Google Wallet REST API หรือ Android SDK เมื่อสร้างเรียบร้อยแล้ว ระบบจะใช้บัตรเหล่านั้นเพื่อออกบัตรโดยการอ้างอิงบัตร

นอกจากนี้ คุณยังสร้างออบเจ็กต์คลาสและบัตรของบัตร "ทันเวลา" ได้ด้วยการฝัง JSON ของออบเจ็กต์โดยตรงใน JWT ที่ใช้ในการออกบัตรให้กับผู้ใช้ ด้วยวิธีนี้ Google Wallet API จะสร้างออบเจ็กต์คลาสและบัตรต่างๆ ขึ้นเมื่อส่ง JWT ที่ลงนามโดยใช้ปุ่มหรือลิงก์ "เพิ่มลงใน Google Wallet"

ตัวอย่างต่อไปนี้แสดง JWT ที่มีคลาสและออบเจ็กต์ Passes ใหม่ซึ่งกำหนดโดยใช้พร็อพเพอร์ตี้ payload.eventTicketClasses และ payload.eventTicketObjects โปรดสังเกตว่าพร็อพเพอร์ตี้เหล่านี้เป็นอาร์เรย์ จึงยอมรับคลาสหรือออบเจ็กต์ของบัตรอย่างน้อย 1 รายการ คุณอาจระบุเฉพาะออบเจ็กต์ Passes ใหม่ใน JWT ที่อ้างอิงคลาส Passes ที่มีอยู่ตามรหัสของออบเจ็กต์นั้นๆ

แสดงตัวอย่าง JWT

  {
    "iss": "issuer@example.com",
    "aud": "google",
    "typ": "savetowallet",
    "iat": 1696877738,
    "origins": [
      "www.example.com"
    ],
    "payload": {
      "eventTicketClasses": [{
        "id": "ISSUER_ID.EVENT_CLASS_ID",
        "issuerName": "[TEST ONLY] Heraldic Event",
        "localizedIssuerName": {
          "defaultValue": {
            "language": "en-US",
            "value": "[TEST ONLY] Heraldic Event"
          }
        },
        "logo": {
          "sourceUri": {
            "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660"
          },
          "contentDescription": {
            "defaultValue": {
              "language": "en-US",
              "value": "LOGO_IMAGE_DESCRIPTION"
            }
          }
        },
        "eventName": {
          "defaultValue": {
            "language": "en-US",
            "value": "Google Live"
          }
        },
        "venue": {
          "name": {
            "defaultValue": {
              "language": "en-US",
              "value": "Shoreline Amphitheater"
            }
          },
          "address": {
            "defaultValue": {
              "language": "en-US",
              "value": "ADDRESS_OF_THE_VENUE"
            }
          }
        },
        "dateTime": {
          "start": "2023-04-12T11:30"
        },
        "reviewStatus": "UNDER_REVIEW",
        "hexBackgroundColor": "#264750",
        "heroImage": {
          "sourceUri": {
            "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336"
          },
          "contentDescription": {
            "defaultValue": {
              "language": "en-US",
              "value": "HERO_IMAGE_DESCRIPTION"
            }
          }
        }
      }],
      "eventTicketObjects": [{
        "id": "ISSUER_ID.OBJECT_ID",
        "classId": "ISSUER_ID.EVENT_CLASS_ID",
        "state": "ACTIVE",
        "seatInfo": {
          "seat": {
            "defaultValue": {
              "language": "en-us",
              "value": "5"
            }
          },
          "row": {
            "defaultValue": {
              "language": "en-us",
              "value": "G"
            }
          },
          "section": {
            "defaultValue": {
              "language": "en-us",
              "value": "40"
            }
          },
          "gate": {
            "defaultValue": {
              "language": "en-us",
              "value": "3A"
            }
          }
        },
        "barcode": {
          "type": "QR_CODE",
          "value": "BARCODE_VALUE",
          "alternateText": ""
        }
      }]
    }
  }