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

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

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

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

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

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

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

วิธีการ

Passes Class เป็นรูปแบบ JSON และสร้างได้ด้วย Google Wallet REST API, Android SDK หรือในคอนโซลธุรกิจของ Google Wallet

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

{
  "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 แต่ละรายการด้วย ซึ่งจะใช้เพื่ออ้างอิงเมื่อออกบัตรผ่าน

วิธีการ

Passes Object เป็นรูปแบบ 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 Class และ 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 SDK สำหรับ Android ในแอปของคุณเท่านั้นเพื่อปกป้องข้อมูลเข้าสู่ระบบของคุณ

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

Java

  // Create the JWT as a HashMap object
  HashMap<String, Object> claims = new HashMap<String, Object>();
  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<String, Object> payload = new HashMap<String, Object>();
  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

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

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

ตัวอย่างต่อไปนี้แสดง JWT ที่มีคลาส Passes Class และออบเจ็กต์ Passes ใหม่ที่กำหนดไว้โดยใช้พร็อพเพอร์ตี้ payload.eventTicketClasses และ payload.eventTicketObjects โปรดสังเกตว่าพร็อพเพอร์ตี้เหล่านี้เป็นอาร์เรย์ จึงยอมรับคลาส Passes หรือออบเจ็กต์ Passes ได้อย่างน้อย 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": ""
        }
      }]
    }
  }