[secret sauce] มาทำระบบ receipt verification in-app purchase กันเถอะ Part II (android)
สำหรับท่านที่ยังไม่เคยอ่าน Part I ไปจ๊ะ
ในบทความที่แล้วเราเกริ่นถึง in-app และระบบของมันอย่างคร่าว ๆ แล้ว คราวนี้เรามาต่อกันที่ android บ้างครับ
Android
flow ของระบบและการทำงานจะเหมือนเดิมทุกอย่างเราจึงยังคงใช้ service ตัวเดิมในการ verify แต่เราจะเพิ่ม ความสามารถของ verify android เข้าไปแทน
ก่อนอื่นเรามาดู api ที่ต้องใช้งานก่อน
API get Products
https://androidpublisher.googleapis.comandroidpublisher/v3/applications/{PackageName}/purchases/products/{ProductID}/tokens/{PurchaseToken}
API get Subscription
https://androidpublisher.googleapis.comandroidpublisher/v3/applications/{PackageName}/purchases/subscriptions/{ProductID}/tokens/{PurchaseToken}
จาก Endpoint ข้างต้นเราจะเห็นว่าต้องการข้อมูล productId
purchaseToken
และ packageName
productId, purchaseToken และ packageName
หาได้จาก Receipt ที่ client ส่งมาหน้าตาจะเป็นแบบนี้ (key : “json”, value: string receipt)
{
"json":
"{\\\"orderId\\\":\\\"GPA.xxxx-xxxx-xxxx-xxxxx\\\",\\\"packageName\\\":\\\"com.xxx.xxx\\\",\\\"productId\\\":\\\"xxx.xxx.xx\\\",// \\\"purchaseTime\\\":1607721533824,\\\"purchaseState\\\":0,\\\"purchaseToken\\\":\\\"xxxx\\\",// \\\"acknowledged\\\":false}\",\"signature\":\"xxxxx\",\"skuDetails\":\"{\\\"productId\\\":\\\"xxx.xxx.xx\\\",// \\\"type\\\":\\\"inapp\\\",\\\"price\\\":\\\"\\u0e3f29.00\\\",\\\"price_amount_micros\\\":29000000,// \\\"price_currency_code\\\":\\\"THB\\\",\\\"title\\\":\\\"xxx\\\",\\\"description\\\":\\\"xxxxx\\\",// \\\"skuDetailsToken\\\":\\\"AEuhp4IhWdExxxxxxxxxxx\\\"}\"
}
เพื่อให้ง่ายต่อการดู แปลงเป็น struct ในภาษา go ได้ดังนี้
type ReceiptGoogle struct {
OrderID string `json:"orderId"`
PackageName string `json:"packageName"`
ProductID string `json:"productId"`
PurchaseState int `json:"purchaseState"`
PurchaseTime int64 `json:"purchaseTime"`
PurchaseToken string `json:"purchaseToken"`
}
คราวนี้เราก็จะได้ param ไป post api แล้ว
แต่! api ข้างต้น requires authenticated
Authenticated Oauth2
เราจะต้องแลก AccessToken มาเพื่อใช้ใน api ต่าง ๆ ของ billing google
เรามาดูวิธีแลก accesstoken กันครับ
Setup
เราจะต้องใช้ client_email
และ private_key
เพื่อจะเอา AccessToken
เราจะต้องมี. Google Service Account ก่อนครับ
Ref: https://developers.google.com/android-publisher/getting_started#using_a_service_account
สร้าง service account
- เข้าไปที่ service account โปรเจ็ค “Google Play Console Developer" (หากไม่มี project ให้สร้างจาก API access ใน Google Play Console)
- + Create service account.
- ใส่ name, description > create and continue
- ข้อ (2) Grant role
Action Viewer
ข้อ (3) ข้ามได้เลยครับ กด DONE
4. หลังจากสร้าง account แล้วให้เราเข้าไปที่ account ที่เราสร้างเมื่อกี้
> Add Key > JSON > Create
** ส่วนนี้เราจะต้องเก็บไฟล์ไว้ให้ดีและปลอดภัยครับ ไม่สามารถโหลดซ้ำได้ ต้องสร้างใหม่เท่านั้น
ตัวอย่างของ file json ที่ได้มา สิ่งที่เราต้องการคือ client_email
และ private_key
นั้นเองงง
ยัง ยังไม่จบเราจะต้อง Grant permission ให้ accounts นี้ก่อนครับ
5. ไปที่ API access ของ Google Play Console เราจะเจอ Service accounts ที่เราสร้างมาเมื่อกี้ กด Grant access
** Account permission
- Role ที่ต้องการ คือ View app information ..(Visibility) / View financial data และ Manage orders and subscriptions
** App permission
- Add app ที่ต้องการให้ permission ครับ
เรียบร้อย ! เราก็จะได้ Service Account ที่สามารถนำไปแลก AccessToken ที่ต้องการได้แล้ว
ถึงเวลา Get Access Token
วิธีการก็จะมีหลากหลายแต่วิธีที่เลือกมาวันนี้คือ ใช้ lib oauth2 ของ google ครับ
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
ใส่ Scopes : https://www.googleapis.com/auth/androidpublisher
นี้คือตัวอย่างการใช้งาน lib ในการ get access token ครับ โดยที่ lib ตัวนี้ จะ cached token ให้ ถ้าไม่ valid จะ new token ให้ใหม่เลยด้วยครับ
Note: clientEmail , privateKey จากไฟล์ json ตอนเราสร้าง service account ครับ
หลักจากได้ AccessToken สุดท้ายหน้าตาของ request ที่ต้องการก็จะเป็นแบบนี้
ถ้า success response จะได้ ประมาณนี้
{
"purchaseTimeMillis": "1621956153498",
"purchaseState": 0,
"consumptionState": 1,
"developerPayload": "",
"orderId": "GPA.3369-2294-3100-xxxx",
"purchaseType": 0,
"acknowledgementState": 1,
"kind": "androidpublisher#productPurchase",
"regionCode": "TH"
}
เพิ่มเติมได้ที่ Purchase Product
response ของ subscription จะเป็นประมาณนี้
{
"startTimeMillis": "1623061338874",
"expiryTimeMillis": "1623061756809",
"autoRenewing": true,
"priceCurrencyCode": "THB",
"priceAmountMicros": "119000000",
"countryCode": "TH",
"developerPayload": "",
"paymentState": 1,
"orderId": "GPA.3329-6601-9591-xxxx",
"purchaseType": 0,
"acknowledgementState": 1,
"kind": "androidpublisher#subscriptionPurchase"
}
เพิ่มเติมได้ที่ subscription
ถ้าหาก error จะได้ response ประมาณนี้
{ "error": { "errors": [ { "domain": "global", "reason": "invalid", "message": "Invalid Value" } ], "code": 400, "message": "Invalid Value" }
Note: PurchaseToken จาก payload ควรใช้เป็น transection ID สามารถใช้ไปอ้างอิงได้ และใช้ประโยชน์ใน api อื่น ๆ ได้อีก เช่น ใช้ในการ clawbacks voided-purchases (รายละเอียดจะมาพูดต่อใน blog เรื่อง Refund ครับ)
นี้คือ flow ทั้งหมดของการ purchase verify ครับ คราวนี้ก็ขึ้นอยู่กับการ design ระบบของเราแล้วละ หวังว่าจะช่วยให้เริ่มต้นได้ง่ายนะครับ ><
หากภาษามนุษย์ไม่เข้าใจสามารถอ่าน code ตัวอย่างได้นี้ที่ครับ
สำหรับ blog ต่อไปเราจะมาต่อกันเรื่อง subscription บ้าง ขอบคุณครับ ^^