1. บทนำ
Codelab นี้สอนวิธีใช้ Google Play Billing Library (PBL) เพื่อจัดการการเปลี่ยนแปลงแพ็กเกจการสมัครใช้บริการ คุณจะได้ทราบว่าโหมดการเปลี่ยนแพ็กเกจต่างๆ ส่งผลต่อราคาและสิทธิ์ของผู้ใช้อย่างไร พร้อมกับเรียนรู้วิธีประมวลผลการแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอป (RTDN) ในแบ็กเอนด์
ผู้ชม
Codelab นี้ออกแบบมาสำหรับนักพัฒนาแอป Android โดยมีคำแนะนำในการใช้ฟีเจอร์การจัดการการสมัครใช้บริการที่ซับซ้อน คำแนะนำนี้จะช่วยให้คุณมอบประสบการณ์การอัปเกรด ดาวน์เกรด หรือเปลี่ยนไปใช้แพ็กเกจการสมัครใช้บริการอื่นได้อย่างราบรื่น
สิ่งที่คุณจะได้เรียนรู้...
- วิธีสร้างการสมัครใช้บริการใน Play Developer Console
- วิธีเลือก
ReplacementModeที่ถูกต้อง (เช่นWITH_TIME_PRORATIONกับDEFERRED) ให้ตรงกับนโยบายการอัปเกรดและดาวน์เกรดของแอป - วิธีกำหนดค่า
BillingFlowParamsภายในlaunchBillingFlowเพื่อทริกเกอร์ขั้นตอนการซื้อใน Google Play สำหรับการแทนที่แพ็กเกจ - วิธีใช้การแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอป (RTDN) และ API
purchases.subscriptionsv2เพื่อเพิกถอนสิทธิ์เข้าถึงเก่าและให้สิทธิ์เข้าถึงใหม่ในแบ็กเอนด์อย่างปลอดภัย
สิ่งที่คุณต้องมี
- สิทธิ์เข้าถึง Google Play Console ด้วยบัญชีนักพัฒนาแอป หากยังไม่มีบัญชีนักพัฒนาแอป คุณจะต้องสร้างบัญชี
- แอปตัวอย่างสำหรับ Codelab นี้ ซึ่งคุณดาวน์โหลดจาก GitHub ได้
- Android Studio
2. สร้างแอปตัวอย่าง
Codelab นี้ใช้แอป Android ตัวอย่างเพื่อแสดงวิธีใช้การแทนที่การสมัครใช้บริการใน PBL แอปตัวอย่างได้รับการออกแบบให้เป็นแอป Android ที่ใช้งานได้อย่างเต็มรูปแบบซึ่งมีซอร์สโค้ดที่สมบูรณ์ซึ่งแสดงลักษณะต่อไปนี้
- การผสานรวมแอปกับ PBL
- สร้างฟังก์ชันเปลี่ยนแพ็กเกจการสมัครใช้บริการ
หากคุ้นเคยกับการแทนที่การสมัครใช้บริการและ PBL อยู่แล้ว คุณสามารถดาวน์โหลดแอปตัวอย่างและทดลองใช้ได้
วิดีโอเดโมต่อไปนี้แสดงลักษณะและการทำงานของแอปตัวอย่างหลังจากที่ติดตั้งใช้งานและเรียกใช้
ข้อกำหนดเบื้องต้น
ก่อนที่จะสร้างและติดตั้งใช้งานแอปตัวอย่าง ให้ทำดังนี้
- สร้างบัญชีนักพัฒนาแอป Google Play Console หากมีบัญชีนักพัฒนาแอปอยู่แล้ว ให้ข้ามขั้นตอนนี้
- สร้างแอปใหม่ใน Play Console โดยเปิดใช้ฟีเจอร์การสร้างรายได้ หรือจะใช้แอปที่มีอยู่แล้วใน Play Console ก็ได้ หากแอปไม่ได้เปิดใช้ฟีเจอร์การสร้างรายได้ ให้ทำตามขั้นตอนเหล่านี้เพื่อตั้งค่า
- ติดตั้ง Android Studio
สร้าง
หากต้องการสร้างแอปตัวอย่างตามที่จำเป็นเพื่อทำตาม Codelab ให้ทำดังนี้
- ดาวน์โหลดแอปตัวอย่างจาก GitHub
- อัปเดต
applicationIdภายในbuild.gradleของแอปตัวอย่างให้ตรงกับ Application Id ของแอปใน Play Developer Console - สร้างแอปตัวอย่าง
หมายเหตุ: การดำเนินการนี้จะสร้างแอปสำหรับการทดสอบในเครื่องได้สำเร็จ อย่างไรก็ตาม การเรียกใช้แอปจะไม่ดึงข้อมูลผลิตภัณฑ์และราคาเนื่องจากยังไม่ได้สร้างการสมัครใช้บริการที่จำเป็นใน Play Developer Console ส่วนถัดไปจะครอบคลุมการสร้างการสมัครใช้บริการใน Developer Console
3. สร้างการสมัครใช้บริการใน Play Console
ระบบการสมัครใช้บริการของ Google Play มอบความยืดหยุ่นในการที่คุณจะสร้าง จัดการ และขายการสมัครใช้บริการ ใน Play Console คุณสามารถกำหนดค่าการสมัครใช้บริการที่มีแพ็กเกจเริ่มต้นหลายแบบ โดยแต่ละแบบมีหลายข้อเสนอได้ ข้อเสนอการสมัครใช้บริการอาจมีรูปแบบการกําหนดราคาและตัวเลือกคุณสมบัติที่หลากหลายได้ สำหรับ Codelab นี้ คุณจะสร้างการสมัครใช้บริการ 3 รายการ ได้แก่ แพ็กเกจพรีเมียม แพ็กเกจพื้นฐาน และแพ็กเกจ Lite ซึ่งจำลองข้อเสนอการสมัครใช้บริการทั่วไปในราคาต่างๆ โดยแต่ละแพ็กเกจจะมีแพ็กเกจเริ่มต้นแบบตามรอบรายเดือนแพ็กเกจเดียว
สร้างการสมัครสมาชิกใหม่
วิธีสร้างการสมัครใช้บริการใหม่
- เปิด Play Console แล้วไปที่หน้าการสมัครใช้บริการ (สร้างรายได้ด้วย Play > ผลิตภัณฑ์ > การสมัครใช้บริการ)
- คลิกสร้างการสมัครใช้บริการ
- ป้อนรายละเอียดการสมัครใช้บริการ
- ProductID : ป้อนรหัสผลิตภัณฑ์ที่ไม่ซ้ำกัน ป้อน
premium_plan - ชื่อ : ป้อนชื่อย่อสำหรับการสมัครใช้บริการ เช่น
Premium Plan
- ProductID : ป้อนรหัสผลิตภัณฑ์ที่ไม่ซ้ำกัน ป้อน
- คลิกสร้าง
สร้าง Base Plan
- เปิด Play Console แล้วไปที่หน้าการสมัครใช้บริการ (สร้างรายได้ด้วย Play > ผลิตภัณฑ์ > การสมัครใช้บริการ)
- คลิกลูกศรขวาข้างการสมัครใช้บริการที่ต้องการสร้างแพ็กเกจเริ่มต้นในนั้นเพื่อดูรายละเอียดการสมัครใช้บริการดังกล่าว
- คลิกเพิ่มแพ็กเกจเริ่มต้น
- ป้อนรหัสแพ็กเกจเริ่มต้น ตัวอย่าง
monthly-auto-renewing - เลือกประเภทเป็นต่ออายุใหม่อัตโนมัติ
- สำหรับแพ็กเกจเริ่มต้นแบบต่ออายุใหม่อัตโนมัติ ให้ตั้งค่าต่อไปนี้
- ช่วงเวลาที่เรียกเก็บเงิน: รายเดือน
- ระยะเวลาผ่อนผัน: 7 วัน
- การเปลี่ยนแปลงแพ็กเกจการเรียกเก็บเงินและข้อเสนอ: เรียกเก็บเงินในวันที่เรียกเก็บเงิน
- สมัครใช้บริการอีกครั้ง: อนุญาต
- ในส่วนราคาและความพร้อมให้บริการ ให้คลิกกำหนดราคาเพื่อกำหนดราคาของแพ็กเกจเริ่มต้น
- เลือกประเทศและภูมิภาคทั้งหมด แล้วคลิกกำหนดราคา
- ตั้งราคาเป็น $10 สำหรับแพ็กเกจเริ่มต้นนี้ แล้วคลิกอัปเดต
- เมื่อตั้งราคาสำหรับแพ็กเกจเริ่มต้นแล้ว ให้คลิกบันทึกที่ด้านขวาล่าง แล้วคลิกเปิดใช้งาน
สร้างการสมัครใช้บริการสำหรับแอปตัวอย่าง
เพื่อวัตถุประสงค์ของ Codelab นี้ ให้สร้างการสมัครใช้บริการเพิ่มเติม 2 รายการที่มีการกำหนดค่าต่อไปนี้
- แพ็กเกจพื้นฐาน
- รหัสผลิตภัณฑ์: basic_plan
- ชื่อ: แพ็กเกจ Basic
- รหัสแพ็กเกจเริ่มต้น: monthly-auto-renewing
- ราคา: $5
- แพ็กเกจ Lite
- รหัสผลิตภัณฑ์: lite_plan
- ชื่อ: Lite Plan
- รหัสแพ็กเกจเริ่มต้น: monthly-auto-renewing
- ราคา: $3
แอปตัวอย่างได้รับการกำหนดค่าให้ใช้รหัสผลิตภัณฑ์และรหัสแพ็กเกจเริ่มต้นเหล่านี้ คุณสร้างการสมัครใช้บริการที่แตกต่างกันด้วยการกำหนดค่าที่แตกต่างกันได้ ในกรณีนี้ คุณจะต้องแก้ไขแอปตัวอย่างเพื่อใช้รหัสผลิตภัณฑ์ที่คุณสร้างขึ้น
วิดีโอการสร้างการสมัครใช้บริการ
วิดีโอต่อไปนี้แสดงขั้นตอนที่อธิบายไว้ก่อนหน้านี้ในการสร้างการสมัครใช้บริการใน Play Developer Console
4. การเปลี่ยนการสมัครใช้บริการ
นักพัฒนาแอปที่ผสานรวมกับ PBL สามารถมอบตัวเลือกต่างๆ ให้แก่สมาชิกปัจจุบันเพื่อเปลี่ยนแพ็กเกจการสมัครใช้บริการให้ตรงกับความต้องการมากขึ้นได้โดยทำดังนี้
- หากขายการสมัครใช้บริการหลายระดับ เช่น การสมัครใช้บริการขั้นพื้นฐานและพรีเมียม คุณสามารถอนุญาตให้ผู้ใช้เปลี่ยนระดับได้โดยการซื้อแพ็กเกจเริ่มต้นหรือข้อเสนอของการสมัครใช้บริการอื่น
- คุณอนุญาตให้ผู้ใช้เปลี่ยนช่วงเวลาที่เรียกเก็บเงินปัจจุบันได้ เช่น เปลี่ยนจากแพ็กเกจรายเดือนเป็นแพ็กเกจรายปี
- นอกจากนี้ คุณยังอนุญาตให้ผู้ใช้เปลี่ยนแพ็กเกจระหว่างแบบต่ออายุใหม่อัตโนมัติกับแบบชำระล่วงหน้าได้ด้วย
เมื่อผู้ใช้ตัดสินใจอัปเกรด ดาวน์เกรด หรือเปลี่ยนการสมัครใช้บริการ คุณจะต้องระบุโหมดการแทนที่ที่จะกำหนดวิธีใช้มูลค่าตามสัดส่วนของช่วงเวลาที่เรียกเก็บเงินปัจจุบัน และเวลาที่ผู้ใช้จะได้รับการเปลี่ยนแปลงสิทธิ์
Play Billing Library มีตัวเลือก ReplacementMode หลายตัวเลือกในการควบคุมลักษณะการทำงานนี้
โหมดการแทนที่ที่ใช้ได้
WITH_TIME_PRORATION: ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการทันที ระบบจะปรับเวลาที่เหลือตามส่วนต่างของราคาและเครดิตสำหรับการสมัครใช้บริการใหม่โดยการอัปเดตวันที่เรียกเก็บเงินครั้งถัดไป นี่คือลักษณะการทำงานเริ่มต้นCHARGE_PRORATED_PRICE: ระบบจะอัปเกรดรายการการสมัครใช้บริการทันที และรอบการเรียกเก็บเงินจะยังคงเหมือนเดิม จากนั้นระบบจะเรียกเก็บเงินส่วนต่างของราคาสำหรับระยะเวลาที่เหลือจากผู้ใช้CHARGE_FULL_PRICE: ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการทันที และจะเรียกเก็บเงินจากผู้ใช้ในราคาเต็มสำหรับสิทธิ์ใหม่นี้ทันที ระบบจะโอนมูลค่าคงเหลือจากการสมัครใช้บริการก่อนหน้าไปยังสิทธิ์เดียวกัน หรือจะคิดตามสัดส่วนเวลาเมื่อเปลี่ยนไปใช้สิทธิ์อื่นWITHOUT_PRORATION: ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการทันที และจะเรียกเก็บเงินในราคาใหม่เมื่อการสมัครใช้บริการต่ออายุ รอบการเรียกเก็บเงินจะยังคงเหมือนเดิมDEFERRED: ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการเมื่อต่ออายุการสมัครใช้บริการเท่านั้น
5. WITH_TIME_PRORATION
ในโหมดการเปลี่ยนทดแทนนี้ ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการทันที ระบบจะปรับเวลาที่เหลืออยู่ตามส่วนต่างของราคา และจะให้เครดิตสำหรับการสมัครใช้บริการใหม่โดยเลื่อนวันที่เรียกเก็บเงินครั้งถัดไป นี่คือลักษณะการทำงานเริ่มต้น
สถานการณ์ตัวอย่าง
ผู้ใช้เปลี่ยนจากแพ็กเกจ Basic ($4.99 ต่อเดือน) เป็นแพ็กเกจ Premium ($9.99 ต่อเดือน) ในวันที่ 15 เมษายน ซึ่งเป็นช่วงครึ่งรอบการเรียกเก็บเงินรายเดือน
ในสถานการณ์นี้จะมีผลดังต่อไปนี้
- ผู้ใช้จะได้รับสิทธิ์เข้าถึงแพ็กเกจ Premium ทันที
- Google Play จะคำนวณระยะเวลาตามสัดส่วนโดยอัตโนมัติ สมมติว่า Play คำนวณแล้วว่าแพ็กเกจพื้นฐานที่เหลืออีก 15 วันมีมูลค่าเท่ากับแพ็กเกจพรีเมียม 7 วัน ระบบจะเลื่อนวันที่เรียกเก็บเงินครั้งถัดไปเป็นวันที่ 21 เมษายน
- ผู้ใช้ไม่จำเป็นต้องชำระเงินทันที
ข้อมูลโค้ด
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
อัปเกรดด้วย WITH_TIME_PRORATION
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Premium
ระบบจะอัปเกรดสิทธิ์ของผู้ใช้เป็นแพ็กเกจ Premium ทันที จำนวนเงินที่ผู้ใช้ต้องชำระทันทีคือ $0.00 ระบบจะนำมูลค่าที่เหลือของแพ็กเกจ Basic มาคำนวณตามสัดส่วนเป็นเวลาสำหรับแพ็กเกจ Premium ซึ่งจะเลื่อนวันที่ต่ออายุครั้งถัดไปให้เร็วขึ้น ระบบจะเรียกเก็บเงินค่าต่ออายุจากผู้ใช้เป็นจำนวน $9.99 ในวันที่เรียกเก็บเงินที่ปรับใหม่
ลดรุ่นด้วย WITH_TIME_PRORATION
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Lite
ระบบจะลดระดับสิทธิ์ของผู้ใช้เป็นแพ็กเกจ Lite ทันที จำนวนเงินที่ต้องชำระทันทีคือ $0.00 ระบบจะนำมูลค่าที่เหลือของแพ็กเกจ Basic มาคำนวณตามสัดส่วนเป็นเวลาสำหรับแพ็กเกจ Lite ซึ่งจะขยายวันที่ต่ออายุครั้งถัดไปอย่างมาก ระบบจะเรียกเก็บเงินค่าต่ออายุจากผู้ใช้เป็นจำนวน $2.99 ในวันที่เรียกเก็บเงินที่ปรับใหม่
บทสรุป
ในส่วนนี้ คุณได้เรียนรู้วิธีที่ WITH_TIME_PRORATION แก้ไขสิทธิ์ของผู้ใช้โดยไม่มีการเรียกเก็บเงินทันทีด้วยการปรับเวลาจนกว่าจะถึงการต่ออายุครั้งถัดไปตามส่วนต่างของราคา ซึ่งเป็นกลยุทธ์เริ่มต้นที่มีประสิทธิภาพในการอัปเกรดหรือดาวน์เกรดผู้ใช้
6. CHARGE_PRORATED_PRICE
ในโหมดการแทนที่นี้ ระบบจะอัปเกรดรายการการสมัครใช้บริการทันที และรอบการเรียกเก็บเงินจะยังคงเหมือนเดิม จากนั้นระบบจะเรียกเก็บเงินส่วนต่างของราคาสำหรับระยะเวลาที่เหลือจากผู้ใช้
หมายเหตุ: ตัวเลือกนี้ใช้ได้กับการอัปเกรดรายการการสมัครใช้บริการเท่านั้น ซึ่งราคาต่อหน่วยเวลาจะเพิ่มขึ้น
สถานการณ์ตัวอย่าง
ผู้ใช้แพ็กเกจ Basic ($4.99 ต่อเดือน) ตัดสินใจอัปเกรดเป็นแพ็กเกจ Premium ($9.99 ต่อเดือน) ในวันที่ 20 เมษายน โดยมีระยะเวลาเหลืออีกประมาณ 10 วันในรอบการเรียกเก็บเงินรายเดือน
ในสถานการณ์นี้จะมีผลดังต่อไปนี้
- ผู้ใช้จะได้รับสิทธิ์เข้าถึงแพ็กเกจ Premium ทันที
- ระบบจะเรียกเก็บเงินจากผู้ใช้ทันทีตามส่วนต่างที่คิดตามสัดส่วนสำหรับ 10 วันที่เหลือของรอบการเรียกเก็บเงินปัจจุบัน ซึ่งคิดเป็นเงินประมาณ $2.99 ซึ่งเป็นค่าแพ็กเกจ Premium เป็นเวลา 10 วัน
- วันที่เรียกเก็บเงินสำหรับผู้ใช้จะไม่เปลี่ยนแปลง
ข้อมูลโค้ด
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
อัปเกรดด้วย CHARGE_PRORATED_PRICE
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Premium
ระบบจะอัปเกรดผู้ใช้เป็นแพ็กเกจ Premium ทันที โดยจะยังคงใช้วันที่ต่ออายุเดิม จำนวนเงินที่ต้องชำระทันทีคือส่วนต่างตามสัดส่วนระหว่างราคาแพ็กเกจ Premium และ Basic สำหรับวันที่เหลือของรอบปัจจุบัน เมื่อถึงวันที่ต่ออายุ ระบบจะเรียกเก็บเงินจากผู้ใช้ในจำนวนเต็มสำหรับการต่ออายุ Premium ซึ่งก็คือ $9.99
ดาวน์เกรดด้วย CHARGE_PRORATED_PRICE
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Lite
โหมดการแทนที่นี้จะทำให้เกิดข้อผิดพลาดระหว่างการดาวน์เกรดเนื่องจากใช้ได้กับการอัปเกรดรายการการสมัครใช้บริการที่ราคาต่อหน่วยเวลาเพิ่มขึ้นเท่านั้น ขั้นตอนการเรียกเก็บเงินจะล้มเหลวและแสดงข้อผิดพลาดแก่ผู้ใช้โดยระบุว่าระบบไม่รองรับโหมดการปันส่วนสำหรับการลดรุ่น
บทสรุป
ส่วนนี้อธิบายวิธีที่ CHARGE_PRORATED_PRICE อนุญาตให้อัปเกรดได้ทันทีโดยการเรียกเก็บเงินจากผู้ใช้ตามส่วนต่างของราคาที่แน่นอนสำหรับช่วงเวลาที่เรียกเก็บเงินที่เหลืออยู่ ในขณะที่รอบการเรียกเก็บเงินยังคงเหมือนเดิม ซึ่งจะมีประโยชน์เมื่อผู้ใช้ต้องการอัปเกรดเป็นแพ็กเกจที่แพงขึ้นโดยไม่เปลี่ยนวันที่เรียกเก็บเงิน
7. CHARGE_FULL_PRICE
ในโหมดการแทนที่นี้ ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการทันที และจะเรียกเก็บเงินจากผู้ใช้ในราคาเต็มสำหรับสิทธิ์ใหม่ทันที ระบบจะโอนมูลค่าคงเหลือจากการสมัครใช้บริการก่อนหน้าไปยังสิทธิ์เดียวกัน หรือจะคิดตามสัดส่วนเวลาเมื่อเปลี่ยนไปใช้สิทธิ์อื่น
สถานการณ์ตัวอย่าง
ผู้ใช้ใช้แพ็กเกจ Basic ($4.99 ต่อเดือน เริ่มตั้งแต่วันที่ 1 เมษายน) ในวันที่ 20 เมษายน ผู้ใช้ต้องการเปลี่ยนไปใช้แพ็กเกจ Premium ($9.99 ต่อเดือน)
ในสถานการณ์นี้จะมีผลดังต่อไปนี้
- ระบบจะเรียกเก็บเงินจากผู้ใช้ในราคาเต็มของแพ็กเกจ Premium ($9.99) ทันที
- ระบบจะแปลงมูลค่าที่เหลือจากแพ็กเกจ Basic (เช่น มูลค่า 10 วัน) เป็นเวลาเทียบเท่าสำหรับแพ็กเกจ Premium ในตัวอย่างนี้ Basic 10 วันจะเทียบเท่ากับ Premium 5 วัน
- ระบบจะปรับวันที่ต่ออายุครั้งถัดไปของผู้ใช้ให้รวมเวลาตามสัดส่วนนี้ ดังนั้น วันที่ต่ออายุจึงเป็นวันที่ 25 พฤษภาคม (20 เมษายน + 1 เดือน + 5 วัน)
- การต่ออายุหลังจากนี้จะเกิดขึ้นทุกเดือนโดยเริ่มตั้งแต่วันที่ 25 พฤษภาคม
ข้อมูลโค้ด
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
อัปเกรดด้วย CHARGE_FULL_PRICE
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Premium
ระบบจะอัปเกรดผู้ใช้เป็นแพ็กเกจ Premium ทันที จำนวนเงินที่ต้องชำระทันทีคือราคาเต็มของแพ็กเกจ Premium ซึ่งอยู่ที่ $9.99 ระบบจะแปลงมูลค่าที่เหลือจากแพ็กเกจพื้นฐานเป็นเวลาในแพ็กเกจ Premium ใหม่ ซึ่งจะขยายวันที่ต่ออายุครั้งแรกออกไปเล็กน้อย หลังจากนั้น ระบบจะเรียกเก็บเงินสำหรับการต่ออายุรอบละ $9.99
ดาวน์เกรดด้วย CHARGE_FULL_PRICE
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Lite
ระบบจะลดรุ่นผู้ใช้เป็นแพ็กเกจ Lite ทันทีและจะเริ่มรอบการเรียกเก็บเงินใหม่ จำนวนเงินที่ต้องชำระทันทีคือราคาเป้าหมายเต็มจำนวน $2.99 ระบบจะเฉลี่ยสัดส่วนที่ไม่ได้ใช้ของแพ็กเกจ Basic เป็นเวลาสำหรับแพ็กเกจ Lite ใหม่ ซึ่งจะขยายวันที่ต่ออายุครั้งแรก หลังจากนั้น ค่าต่ออายุจะอยู่ที่ $2.99 ต่อรอบ
บทสรุป
ในส่วนนี้ เราได้อธิบายวิธีที่ CHARGE_FULL_PRICE เรียกเก็บเงินจากผู้ใช้เต็มจำนวนในวันที่เปลี่ยนแพ็กเกจ ซึ่งจะเริ่มรอบการเรียกเก็บเงินใหม่ทันที ยอดคงเหลือที่เหลือจากแพ็กเกจก่อนหน้าจะนำไปใช้กับวันที่ต่ออายุถัดไปตามสัดส่วน
8. WITHOUT_PRORATION
ในโหมดการแทนที่นี้ ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการทันที และจะเรียกเก็บเงินในราคาใหม่เมื่อการสมัครใช้บริการต่ออายุ
สถานการณ์ตัวอย่าง
ผู้ใช้ใช้แพ็กเกจ Basic ($4.99 ต่อเดือน เริ่มตั้งแต่วันที่ 1 เมษายน) ในวันที่ 20 เมษายน ผู้ใช้ต้องการเปลี่ยนไปใช้แพ็กเกจ Premium ($9.99 ต่อเดือน)
ในสถานการณ์นี้จะมีผลดังต่อไปนี้
- ผู้ใช้จะได้รับสิทธิ์เข้าถึงแพ็กเกจ Premium ทันที
- ผู้ใช้ไม่ต้องจ่ายในราคาที่สูงขึ้นที่ $9.99 จนกว่าจะถึงวันที่ต่ออายุการสมัครใช้บริการครั้งถัดไป (1 พฤษภาคม)
ข้อมูลโค้ด
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
อัปเกรดด้วย WITHOUT_PRORATION
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Premium
ระบบจะอัปเกรดผู้ใช้เป็นแพ็กเกจ Premium ทันทีพร้อมคงวันที่ต่ออายุเดิมไว้ จำนวนเงินที่ต้องชำระทันทีคือ $0.00 ผู้ใช้จะมีสิทธิ์เข้าถึงแพ็กเกจ Premium ในรอบที่กำหนดเป็นระยะเวลาที่เหลือ ก่อนที่จะเปลี่ยนไปใช้จำนวนเงินต่ออายุใหม่ที่ $9.99 ในวันที่เรียกเก็บเงินครั้งถัดไป
ดาวน์เกรดโดยไม่มีการปันส่วน
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Lite
ระบบจะดาวน์เกรดผู้ใช้เป็นแพ็กเกจ Lite ทันที และผู้ใช้จะเสียสิทธิ์เข้าถึงฟีเจอร์ Basic ที่ชำระเงินไปแล้ว จำนวนเงินที่ต้องชำระทันทีคือ $0.00 รอบการเรียกเก็บเงินจะยังคงเหมือนเดิม และผู้ใช้จะชำระเงินในราคาใหม่ที่ต่ำกว่าที่ $2.99 ในการต่ออายุที่กำหนดเวลาไว้ครั้งถัดไป
บทสรุป
ส่วนนี้แสดงให้เห็นว่า WITHOUT_PRORATION จะสลับสิทธิ์ของผู้ใช้ทันทีโดยไม่มีการเรียกเก็บเงินในระหว่างการชำระเงิน ในขณะที่รอบการเรียกเก็บเงินยังคงเหมือนเดิม
9. เลื่อน
ในโหมดการแทนที่นี้ ระบบจะอัปเกรดหรือดาวน์เกรดรายการการสมัครใช้บริการเมื่อต่ออายุการสมัครใช้บริการเท่านั้น แต่จะออกการซื้อใหม่ทันที โดยระบบจะตั้งค่าไอเทมที่มีอยู่ให้ต่ออายุไม่ได้และจะหมดอายุเมื่อสิ้นสุดรอบการเรียกเก็บเงินปัจจุบัน ในขณะที่สิทธิ์ใหม่ที่ขอจะเริ่มใช้งานได้ทันทีหลังจากนั้น
สถานการณ์ตัวอย่าง
ผู้ใช้ใช้แพ็กเกจ Basic ($4.99 ต่อเดือน เริ่มตั้งแต่วันที่ 1 เมษายน) ในวันที่ 20 เมษายน ผู้ใช้ต้องการเปลี่ยนไปใช้แพ็กเกจ Premium ($9.99 ต่อเดือน)
ในสถานการณ์นี้จะมีผลดังต่อไปนี้
- ระบบจะไม่เรียกเก็บเงินจากผู้ใช้ทันที
- ผู้ใช้จะยังคงได้รับฟีเจอร์ Basic จนกว่าจะสิ้นสุดรอบการเรียกเก็บเงินปัจจุบัน (30 เมษายน)
- ระบบจะอัปเกรดแพ็กเกจการสมัครใช้บริการเป็น Premium โดยอัตโนมัติในวันที่ต่ออายุครั้งถัดไป (1 พฤษภาคม)
ข้อมูลโค้ด
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.DEFERRED;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
อัปเกรดด้วย DEFERRED
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.DEFERRED - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Premium
ผู้ใช้จะยังคงใช้แพ็กเกจ Basic จนกว่าจะสิ้นสุดรอบการเรียกเก็บเงินปัจจุบัน จำนวนเงินที่ต้องชำระทันทีคือ $0.00 เมื่อถึงวันที่ต่ออายุ สิทธิ์ของลูกค้าจะอัปเกรดเป็นแพ็กเกจ Premium และระบบจะเรียกเก็บเงินค่าต่ออายุใหม่จำนวน $9.99
ดาวน์เกรดด้วย DEFERRED
วิธีจำลองสถานการณ์นี้
- ใน
MainActivityของแอปตัวอย่าง ให้อัปเดตreplacementModeในข้อมูลโค้ดเป็นSubscriptionProductReplacementParams.ReplacementMode.DEFERRED - สร้างและเปิดใช้แอปพลิเคชันอีกครั้ง
- ยกเลิกการสมัครใช้บริการที่มีอยู่ (หากมี) จาก Google Play Store และการสมัครใช้บริการจะหมดอายุ
- ซื้อแพ็กเกจ Basic
- เปลี่ยนไปใช้แพ็กเกจ Lite
ผู้ใช้จะยังคงใช้แพ็กเกจ Basic จนกว่าจะสิ้นสุดรอบการเรียกเก็บเงินปัจจุบัน จำนวนเงินที่ต้องชำระทันทีคือ $0.00 เมื่อถึงวันที่ต่ออายุ สิทธิ์ของลูกค้าจะอัปเกรดเป็นแพ็กเกจ Lite และระบบจะเรียกเก็บเงินค่าต่ออายุใหม่จำนวน $2.99
บทสรุป
ส่วนนี้อธิบายว่าโหมดการแทนที่ DEFERRED จะเลื่อนการอัปเกรดหรือดาวน์เกรดจนกว่าจะสิ้นสุดระยะเวลาที่ผู้ใช้ที่ใช้งานอยู่ชำระเงินแล้วได้อย่างไร ซึ่งเหมาะอย่างยิ่งสำหรับการลดรุ่นเพื่อไม่ให้เสียฟีเจอร์ที่ซื้อไปแล้ว
10. การประมวลผลฝั่งแบ็กเอนด์และฝั่งไคลเอ็นต์
หลังจากที่ผู้ใช้ทริกเกอร์การแทนที่การสมัครใช้บริการที่สำเร็จแล้ว ให้ตรวจสอบว่าทั้งแอปและแบ็กเอนด์ของคุณจัดการการเปลี่ยนแปลงอย่างถูกต้องเพื่อหลีกเลี่ยงปัญหาต่างๆ เช่น การหยุดชะงักของบริการหรือการเรียกเก็บเงินซ้ำ
สถานการณ์ตัวอย่าง
- ผู้ใช้มีแพ็กเกจรายเดือนพื้นฐาน (product_id
basic_planและ purchase_tokenbasic_purchase_token_123) - ผู้ใช้เปลี่ยนไปใช้แพ็กเกจ Premium โดยใช้โหมดการเปลี่ยนแทนทันที (
WITHOUT_PRORATION,WITH_TIME_PRORATION,CHARGE_PRORATED_PRICE,CHARGE_FULL_PRICEอย่างใดอย่างหนึ่ง) - เมื่อเปลี่ยนการสมัครใช้บริการสำเร็จแล้ว Google จะถือว่าเป็นการสมัครใช้บริการใหม่ และสร้างโทเค็นการซื้อใหม่ที่แตกต่างกันสำหรับแพ็กเกจ Premium (product_id
premium_planและ purchase_tokenpremium_purchase_token_123)
การประมวลผลฝั่งไคลเอ็นต์
onPurchasesUpdated
เมื่อการซื้อทดแทนเสร็จสมบูรณ์ ระบบจะทริกเกอร์ PurchasesUpdatedListener แม้ว่าจะเป็นการเปลี่ยน แต่ Google Play จะถือว่าแพ็กเกจ Premium เป็นการซื้อใหม่
แอปจะได้รับออบเจ็กต์ Purchase ที่มี premium_purchase_token_123 purchase token และ product_id premium_plan คุณต้องปฏิบัติต่อผู้ใช้รายนี้เหมือนกับสมาชิกใหม่ทุกประการ นั่นคือ ยืนยันโทเค็นและเตรียมให้สิทธิ์เข้าถึง
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) {
// purchase.getPurchaseToken() = premium_purchase_token_123
// purchase.getProducts() will contain premium_plan
// Verify the purchase and grant entitlement
handleNewPurchase(purchase);
}
}
}
queryPurchasesAsync
queryPurchasesAsync จะแสดงเฉพาะการสมัครใช้บริการที่ใช้งานอยู่ซึ่งซื้อจากแอปของคุณ คุณควรใช้วิธีนี้เพื่อพิจารณาสิทธิ์ที่จะแสดงต่อผู้ใช้ สำหรับการเปลี่ยนทดแทนทันที queryPurchasesAsync() จะหยุดส่งคืนโทเค็นการซื้อ BASIC เก่าและจะส่งคืนเฉพาะโทเค็นการซื้อ PREMIUM ใหม่เท่านั้น
เรียกใช้เมธอดนี้ทุกครั้งที่แอปกลับมาทำงานต่อหรือการซื้อเสร็จสมบูรณ์ หากมีโทเค็น Premium ให้มอบสิทธิ์เข้าถึงฟีเจอร์ Premium และนำฟีเจอร์พื้นฐานออกทันที
การประมวลผลแบ็กเอนด์ (RTDN)
เมื่อมีการเปลี่ยนทดแทน Google Play จะส่งการแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอป (RTDN) ไปยังหัวข้อ Pub/Sub ที่กำหนดค่าไว้
- ในกรณีของการเปลี่ยนทดแทนทันที Google จะส่ง
SUBSCRIPTION_PURCHASEDRTDN พร้อมโทเค็นการซื้อใหม่ตัวอย่างเพย์โหลด RTDN{ "version":"1.0", "packageName":"com.google.play.billing.samples.subscriptions", "eventTimeMillis":"...", "subscriptionNotification": { "version":"1.0", "notificationType":4, // SUBSCRIPTION_PURCHASED "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription } } - เมื่อเซิร์ฟเวอร์ได้รับโทเค็นการซื้อใหม่จาก RTDN ให้เรียกใช้ API
purchases.subscriptionsV2ด้วยโทเค็นการซื้อใหม่เพื่อดึงข้อมูลรายละเอียดการซื้อ การตอบกลับของ API มีฟิลด์linkedPurchaseTokenซึ่งใช้เพื่อพิจารณาว่าโทเค็นการซื้อหมายถึงการสมัครใช้บริการใหม่หรือการแทนที่การสมัครใช้บริการ - ในกรณีของการแทนที่การสมัครใช้บริการ
linkedPurchaseTokenหมายถึงโทเค็นการซื้อของการสมัครใช้บริการเดิม ในสถานการณ์นี้จะเป็นbasic_purchase_token_123.SampleGET purchases.subscriptionsV2responsecurl \ 'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/<application_id>/purchases/subscriptionsv2/tokens/premium_purchase_token_123' \ --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \ --header 'Accept: application/json' { "kind": "androidpublisher#subscriptionPurchaseV2", "startTime": "...", "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE", "latestOrderId": "GPA.<order_id>", "linkedPurchaseToken": "basic_purchase_token_123", // The purchase token of the subscription that was replaced (Basic Plan in this case) "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "lineItems": [ { "productId": "premium_plan", // productID of the new subscription (Premium Plan in this case) "expiryTime": "....", "autoRenewingPlan": {...}, "offerDetails": { "basePlanId": "monthly-auto-renewing" // base plan ID of the new subscription }, "itemReplacement": { // Details about the subscription replacement "productId": "subscription_basic", // productID of the old subscription (Basic Plan in this case) "replacementMode": "CHARGE_PRORATED_PRICE", // Replacement strategy used for this subscription change "basePlanId": "monthly-auto-renewing" // base plan ID of the old subscription }, "offerPhase": {...} } ], "etag": "<etag_value>" } - คุณต้องรับทราบการซื้อ Premium ใหม่ ซึ่งทำได้ทั้งภายในแอปหรือในแบ็กเอนด์ หากไม่รับทราบการซื้อภายใน 3 วัน ระบบจะคืนเงินและเพิกถอนสิทธิ์ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการประมวลผลและการรับทราบการซื้อได้ในเอกสารประกอบสําหรับนักพัฒนาซอฟต์แวร์
บทสรุป
ส่วนนี้ครอบคลุมขั้นตอนการจัดการการแทนที่การสมัครใช้บริการทันทีทั้งในฝั่งไคลเอ็นต์และแบ็กเอนด์ของคุณ คุณทราบว่า Google Play จะถือว่าแพ็กเกจใหม่เป็นการซื้อใหม่ทั้งหมดและออกโทเค็นการซื้อใหม่ ในไคลเอ็นต์ คุณต้องประมวลผลการซื้อใหม่นี้โดยใช้ PurchasesUpdatedListener และอัปเดตสิทธิ์ตามการตอบกลับจาก queryPurchasesAsync ในแบ็กเอนด์ คุณควรฟัง SUBSCRIPTION_PURCHASED RTDN สำหรับโทเค็นใหม่ ใช้ API purchases.subscriptionsv2 เพื่อระบุ linkedPurchaseToken ของการสมัครใช้บริการเก่า และเพิกถอนสิทธิ์เข้าถึงที่เชื่อมโยงกับโทเค็นเก่าทันทีขณะให้สิทธิ์ใหม่ โปรดอย่าลืมรับทราบการซื้อใหม่เสมอ
11. ประมวลผลการเปลี่ยนทดแทนที่เลื่อนออกไป
ReplacementMode.DEFERRED จะเลื่อนการเปลี่ยนแปลงการสมัครใช้บริการและการอัปเดตสิทธิ์จนกว่าจะสิ้นสุดรอบการเรียกเก็บเงินปัจจุบัน ซึ่งแตกต่างจากโหมดการแทนที่ทันที การจัดการการเปลี่ยนทดแทนที่เลื่อนออกไปต้องใช้ตรรกะที่เฉพาะเจาะจงเพื่อให้มั่นใจว่าผู้ใช้จะได้รับสิทธิ์ที่ถูกต้องในเวลาที่เหมาะสม
สถานการณ์ตัวอย่าง
- ผู้ใช้มีแพ็กเกจรายเดือนพื้นฐาน (product_id
basic_planและ purchase_tokenbasic_purchase_token_123) ซึ่งจะต่ออายุในวันที่ 15 เมษายน - ในวันที่ 1 เมษายน ผู้ใช้ตัดสินใจเปลี่ยนไปใช้แพ็กเกจ Premium โดยใช้
ReplacementMode.DEFERRED - Google จะสร้างโทเค็นการซื้อใหม่สำหรับแพ็กเกจ Premium (product_id
premium_planและ purchase_tokenpremium_purchase_123) ทันที แต่จะกำหนดเวลาเรียกเก็บเงินจากผู้ใช้และสิทธิ์เป็นวันที่ 15 เมษายน
ดำเนินการเปลี่ยนทดแทนที่เลื่อนออกไป
1. ทันทีหลังจากขั้นตอนการซื้อสำเร็จ (แอป)
PurchasesUpdatedListenerจะเรียกใช้หลังจากขั้นตอนการซื้อเสร็จสมบูรณ์ แอปจะได้รับออบเจ็กต์Purchaseที่มีโทเค็นการซื้อใหม่premium_purchase_token_123แต่ product_id จะยังคงอ้างอิงถึงbasic_planเดิมเนื่องจากผู้ใช้มีสิทธิ์ใช้แพ็กเกจ Basic เท่านั้น คุณต้องถือว่าการดำเนินการนี้เป็นการซื้อใหม่และรับทราบโทเค็น@Override public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { for (Purchase purchase : purchases) { // purchase.getPurchaseToken() = premium_purchase_token_123 // purchase.getProducts() will contain basic_plan // Verify and acknowledge the purchase handleNewPurchase(purchase); } } }queryPurchasesAsyncจะแสดงผลการซื้อพร้อมโทเค็นการซื้อใหม่ (premium_purchase_token_123) ทันที รวมถึงสิทธิ์เดิม (basic_plan) ที่เชื่อมโยงกับการซื้อนั้น คุณสามารถใช้การตั้งค่านี้เพื่อมอบสิทธิ์ในแพ็กเกจพื้นฐานให้แก่ผู้ใช้ต่อไปได้
2. ทันทีหลังจากขั้นตอนการซื้อสำเร็จ (แบ็กเอนด์)
- ระบบจะส่ง RTDN SUBSCRIPTION_PURCHASED ทันทีหลังจากขั้นตอนการซื้อสำหรับโทเค็นการซื้อใหม่ (
premium_purchase_token_123) ตัวอย่างเพย์โหลด RTDN{ "version":"1.0", "packageName":"com.google.play.billing.samples.subscriptions", "eventTimeMillis":"...", "subscriptionNotification": { "version":"1.0", "notificationType":4, // SUBSCRIPTION_PURCHASED "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription } } - เรียกใช้
GET purchases.subscriptionsv2ด้วยโทเค็นการซื้อใหม่เพื่อดึงรายละเอียดการซื้อ การตอบกลับมีรายการโฆษณา 2 รายการ- รายการหนึ่งแสดงถึงการสมัครใช้บริการเดิม (แพ็กเกจพื้นฐาน) และมี
expiryTimeในอนาคต ระบบจะไม่ต่ออายุการสมัครใช้บริการเดิมและมีdeferredItemReplacementที่มีการสมัครใช้บริการใหม่ (แพ็กเกจ Premium) ซึ่งแสดงถึงการแทนที่สิทธิ์เดิมที่รอดำเนินการเมื่อสิทธิ์หมดอายุ - เส้นหนึ่งแสดงถึงการสมัครใช้บริการที่เพิ่งซื้อใหม่ ไม่ได้ตั้งค่าสำหรับ "expiryTime"
{ "kind": "androidpublisher#subscriptionPurchaseV2", "startTime": "2026-05-07T15:50:11.383Z", "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE", "latestOrderId": "GPA.<order_id>", "linkedPurchaseToken": "basic_purchase_token_123", "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "lineItems": [ { "productId": "premium_plan", // Premium Plan has no expiry time "autoRenewingPlan": {...}, "offerDetails": {...}, "itemReplacement": {. // Subscription replacement details "productId": "basic_plan", "replacementMode": "DEFERRED", "basePlanId": "monthly-auto-renewing" }, "offerPhase": {} }, { "productId": "basic_plan", // Subscription to be replaced "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time in the future "autoRenewingPlan": {}, "offerDetails": {...}, "deferredItemReplacement": { // identifier indicating this subscription will be replaced upon renewal "productId": "subscription_premium" }, "latestSuccessfulOrderId": "GPA.<order_id>", "itemReplacement": {...}, } ], "etag": "<etag>" } - รายการหนึ่งแสดงถึงการสมัครใช้บริการเดิม (แพ็กเกจพื้นฐาน) และมี
- คุณต้องรับทราบโทเค็นการซื้อใหม่ ซึ่งทำได้ทั้งภายในแอปหรือในแบ็กเอนด์ ดูรายละเอียดเพิ่มเติมเกี่ยวกับการประมวลผลและการรับทราบการซื้อได้ในเอกสารประกอบสําหรับนักพัฒนาซอฟต์แวร์
- SUBSCRIPTION_EXPIRED ระบบจะส่ง RTDN สำหรับโทเค็นการซื้อเก่า (
basic_purchase_token_123) ตัวอย่างเพย์โหลด RTDN{ "version":"1.0", "packageName":"com.google.play.billing.samples.subscriptions", "eventTimeMillis":"...", "subscriptionNotification": { "version":"1.0", "notificationType":13, // SUBSCRIPTION_EXPIRED "purchaseToken":"basic_purchase_token_123" //purchase token for the old subscription } } - เมื่อเรียกใช้ API
GET purchases.subscriptionsv2ด้วยโทเค็นการซื้อเก่า ระบบจะแสดงว่าโทเค็นหมดอายุแล้ว (SUBSCRIPTION_STATE_EXPIRED) สิทธิ์ของแพ็กเกจเก่าจะโอนไปยังการซื้อใหม่สำหรับเวลาที่เหลือ
3. ในวันที่เปลี่ยนแทน - การต่ออายุครั้งแรกหลังจากขั้นตอนการซื้อ (แอป)
queryPurchasesAsyncจะคืนค่าการซื้อพร้อมโทเค็นการซื้อใหม่ (premium_purchase_token_123) และการสมัครใช้บริการใหม่ที่เชื่อมโยงกับโทเค็นดังกล่าว (premium_plan)- ระบบควรประมวลผลการซื้อใหม่แล้วเมื่อขั้นตอนการซื้อสำเร็จ คุณไม่จำเป็นต้องดำเนินการใดๆ เป็นพิเศษนอกเหนือจากการตรวจสอบว่าผู้ใช้ได้รับสิทธิ์เข้าถึงการสมัครใช้บริการที่ถูกต้อง
4. ในวันที่เปลี่ยนแทน - การต่ออายุครั้งแรกหลังจากขั้นตอนการซื้อ (แบ็กเอนด์)
- เมื่อใช้
ReplacementMode.DEFERREDการต่ออายุครั้งแรกจะเป็นไปตามลักษณะการทำงานมาตรฐานของการต่ออายุอื่นๆ ที่ประมวลผลSUBSCRIPTION_RENEWEDRTDN คุณไม่จำเป็นต้องมีตรรกะพิเศษสำหรับการแทนที่เมื่อเกิดกรณีนี้ - เรียกใช้
GET purchases.subscriptionsv2ด้วยโทเค็นการซื้อใหม่เพื่อดึงรายละเอียดการซื้อ การตอบกลับมีรายการโฆษณา 2 รายการ- รายการหนึ่งแสดงถึงการสมัครใช้บริการเดิม (แพ็กเกจพื้นฐาน) และมี
expiryTimeในอดีต การสมัครใช้บริการเดิมจะไม่มีค่าที่ตั้งไว้สำหรับช่องdeferredItemReplacementอีกต่อไป - รายการหนึ่งแสดงการสมัครใช้บริการใหม่ที่มี
expiryTimeในอนาคต และตั้งค่าช่องautoRenewEnabledเป็นtrue
{ "kind": "androidpublisher#subscriptionPurchaseV2", "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE", "latestOrderId": "GPA.<order_id>..0", "linkedPurchaseToken": "basic_purchase_token_123", // purchase token of the old subscription "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "lineItems": [ { "productId": "premium_plan", // New subscription "expiryTime": "2026-05-07T16:00:09.437Z", // Expiry time set in the future "autoRenewingPlan": { "autoRenewEnabled": true, // Auto Renewing Flag set to True "recurringPrice": {...} }, "offerDetails": {...}, "latestSuccessfulOrderId": "GPA.<order_id>..0", "itemReplacement": {. // Details of the subscription replacement "productId": "basic_plan", "replacementMode": "DEFERRED", "basePlanId": "monthly-auto-renewing" }, "offerPhase": {...} }, { "productId": "basic_plan", // Old subscription, Does not contains the deferredItemReplacement field "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time set in the past "autoRenewingPlan": {}, "offerDetails": {...}, "latestSuccessfulOrderId": "GPA.<order_id>..0", "itemReplacement": {...}, } ], "etag": "<etag>" } - รายการหนึ่งแสดงถึงการสมัครใช้บริการเดิม (แพ็กเกจพื้นฐาน) และมี
บทสรุป
ส่วนนี้อธิบายการจัดการที่ไม่ซ้ำกันซึ่งจำเป็นสำหรับ ReplacementMode.DEFERRED คุณทราบว่าการเปลี่ยนแปลงสิทธิ์จะเกิดขึ้นเมื่อสิ้นสุดรอบการเรียกเก็บเงินปัจจุบันเท่านั้น ซึ่งต่างจากโหมดทันที ส่วนนี้ครอบคลุมขั้นตอนที่จำเป็นทั้งสำหรับแอปและแบ็กเอนด์ในการประมวลผลการซื้อครั้งแรกอย่างถูกต้อง รับทราบโทเค็นใหม่ และจัดการการเปลี่ยนสิทธิ์เมื่อการสมัครใช้บริการเก่าหมดอายุและการสมัครใช้บริการใหม่เริ่มใช้งาน
12. สนามเด็กเล่นสำหรับการเปลี่ยนการสมัครใช้บริการ
ฟีเจอร์สนามเด็กเล่นทดแทนในแอปตัวอย่างช่วยให้คุณทดสอบการอัปเกรดและดาวน์เกรดการสมัครใช้บริการสำหรับผลิตภัณฑ์ที่ต้องสมัครใช้บริการซึ่งกำหนดค่าไว้ในบัญชี Google Play Console ได้ ส่วนนี้จะอธิบายวิธีใช้ฟีเจอร์สนามเด็กเล่นการแทนที่
ตั้งค่า
หากต้องการใช้ฟีเจอร์ Playground ทดแทน โปรดตรวจสอบสิ่งต่อไปนี้
packageIdในไฟล์build.gradleตรงกับแอปพลิเคชันที่กำหนดค่าไว้ใน Google Play Console- ระบบจะลงทะเบียนบัญชีผู้ใช้ทดสอบของคุณเป็นผู้ทดสอบสัญญาอนุญาตใน Google Play Console ดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบใบอนุญาตได้ที่ทดสอบการติดตั้งใช้งานการเรียกเก็บเงินของแอป
สนามเด็กเล่นสำหรับการเปลี่ยนการสมัครใช้บริการ
แอปตัวอย่างมีแท็บสนามเด็กเล่นการแทนที่ ซึ่งช่วยให้คุณจำลองการเปลี่ยนแปลงการสมัครใช้บริการได้ คุณสามารถค้นหาข้อมูลการสมัครใช้บริการที่กำหนดไว้ใน Play Console และทดสอบการเปลี่ยนการสมัครใช้บริการโดยใช้โหมดการแทนที่ต่างๆ สนามเด็กเล่นนี้จะช่วยให้คุณเข้าใจว่าโหมดต่างๆ ส่งผลต่อรอบการเรียกเก็บเงินและสิทธิ์ในการสมัครใช้บริการอย่างไร เพื่อให้คุณพิจารณาได้ว่าตัวเลือกใดเหมาะกับความต้องการทางธุรกิจมากที่สุด
หากต้องการจำลองการแทนที่โดยใช้ Playground ให้ทำตามขั้นตอนต่อไปนี้
- ไปที่แท็บสนามเด็กเล่น
- หากมีการสมัครใช้บริการที่ใช้งานอยู่ ระบบจะไฮไลต์การสมัครใช้บริการนั้น นี่คือการสมัครใช้บริการที่จะถูกแทนที่

- หากไม่มีการสมัครใช้บริการที่ใช้งานอยู่ คุณต้องซื้อการสมัครใช้บริการก่อน
- Playground จะแสดงแพ็กเกจ Basic, Premium และ Lite ที่สร้างขึ้นสำหรับ Codelab นี้โดยค่าเริ่มต้น
- หากต้องการทดสอบกับแพ็กเกจอื่นๆ ที่กำหนดค่าไว้ใน Play Developer Console ให้คลิกเพิ่มแพ็กเกจที่กำหนดเอง แล้วค้นหาตาม
productIdและbasePlanId - ซื้อการสมัครใช้บริการที่เลือก
- ตอนนี้การสมัครใช้บริการที่ใช้งานอยู่ซึ่งผู้ใช้เพิ่งซื้อควรจะแสดงแล้ว

- เลือกการสมัครใช้บริการเป้าหมายที่ผู้ใช้ต้องการเปลี่ยนไปใช้
- เลือกโหมดการแทนที่สำหรับการเปลี่ยน

- คลิกปุ่มทดสอบการแทนที่เพื่อจำลองการแทนที่การสมัครใช้บริการ
- คุณควรเห็น Bottom Sheet ของ Google Play Billing พร้อมรายละเอียดที่คำนวณแล้วของการเปลี่ยนแพ็กเกจการสมัครใช้บริการ (เช่น การเรียกเก็บเงินทันทีและการปรับรอบการเรียกเก็บเงิน)

- ทำธุรกรรมให้เสร็จสมบูรณ์
- ไปที่หน้าจัดการการสมัครใช้บริการในแอป Play Store เพื่อดูการเปลี่ยนแปลงการสมัครใช้บริการที่ใช้งานอยู่พร้อมรายละเอียดเกี่ยวกับวันที่ต่ออายุและราคาที่อัปเดตแล้ว

13. ขั้นตอนถัดไป
- ดูวิธีใช้ประโยชน์จากการผสานรวม Play Billing ให้ได้มากที่สุด
- อย่าลืมทำตามแนวทางปฏิบัติแนะนำสำหรับการยืนยันและประมวลผลการซื้อในแบ็กเอนด์ที่ปลอดภัยเมื่อผู้ใช้เริ่มซื้อผลิตภัณฑ์เหล่านี้
เอกสารอ้างอิง
14. ขอแสดงความยินดี
ยินดีด้วย คุณได้ติดตั้งใช้งานการเปลี่ยนแทนการสมัครรับข้อมูลเรียบร้อยแล้วด้วยโหมดการปรับสัดส่วนต่างๆ และกำหนดค่าการจัดการแบ็กเอนด์สำหรับการเปลี่ยนแพ็กเกจ
สิ่งที่คุณได้เรียนรู้
- วิธีกำหนดค่า
SubscriptionProductReplacementParamsด้วยโหมดการแทนที่ที่เฉพาะเจาะจง - ความแตกต่างระหว่างการอัปเกรดทันทีกับการดาวน์เกรดที่เลื่อนออกไป
- วิธีเลิกใช้งานโทเค็นการสมัครใช้บริการเก่าโดยใช้
linkedPurchaseTokenโดยใช้ RTDN
แบบสำรวจ
ความคิดเห็นของคุณเกี่ยวกับ Codelab นี้มีค่าอย่างยิ่ง โปรดสละเวลาสักครู่เพื่อตอบแบบสำรวจของเรา