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

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

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

สำหรับคำแนะนำโดยละเอียดเกี่ยวกับการสร้างบัตร โปรดดูคำแนะนำในการใช้ REST API หรือ Android SDK ของ Google Wallet

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

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

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

การทำงาน

คลาสบัตรจะอยู่ในรูปแบบ 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 ที่เกี่ยวข้อง

คุณต้องกำหนดรหัสที่ไม่ซ้ำกันให้กับออบเจ็กต์ 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 สำหรับผู้ที่ใช้ SDK สำหรับ Android ของ Google Wallet ทาง 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 Class และ Object Object ใหม่จากพร็อพเพอร์ตี้ 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": ""
        }
      }]
    }
  }