يوضّح هذا الدليل كيفية دمج تطبيقك مع واجهات برمجة التطبيقات في Play Billing Library لتتمكّن من توفير خيار الفوترة للمستخدمين.
التكامل مع PBL
يمكنك دمج خيار الفوترة مع PBL في أربعة سيناريوهات. تختلف هذه السيناريوهات استنادًا إلى الجهة التي تعرض شاشة الخيار والمكان الذي ستتم فيه عملية الدفع. يوضّح الجدول التالي سيناريوهات التكامل:
| ما هي شاشة خيار الفوترة التي تريد عرضها؟ | |||
| شاشة Google Play | شاشتك الخاصة (وفقًا لإرشادات تجربة المستخدم) | ||
| أين تتم عملية الدفع؟ | داخل التطبيق | السيناريو 1أ
تعرض Google شاشة الخيار وتتم معالجة نظام الفوترة البديل داخل تطبيقك. |
السيناريو 1ب
يعرض مطوّر التطبيق شاشة الخيار وتتم معالجة نظام الفوترة البديل داخل تطبيقك. |
| رابط ويب خارجي | السيناريو 2أ
تعرض Google شاشة الخيار ويتم توجيه المستخدم إلى خارج تطبيقك إلى مواقعك الإلكترونية لإجراء عمليات الشراء. |
السيناريو 2ب
يعرض مطوّر التطبيق شاشة الخيار ويتم توجيه المستخدم إلى خارج تطبيقك إلى مواقعك الإلكترونية لإجراء عمليات الشراء. |
|
يوضّح الرسم التوضيحي التالي مسار خيار الفوترة لكل من هذه السيناريوهات:
سيناريوهات التكامل مع PBL
استنادًا إلى سيناريو التكامل، اتّبِع الخطوات الواردة في هذا القسم لتنفيذ خيار الفوترة في تطبيقك.
التعامل مع السيناريو 1أ
تعرض Google شاشة الخيار وتتم معالجة نظام الفوترة البديل داخل تطبيقك. اتّبِع الخطوات التالية لتفعيل خيار الفوترة في هذا السيناريو:
استدعِ الدالة
enableBillingProgramمعEnableBillingProgramParamsعند إنشاء مثيل `BillingClient`، ثم ابدأ الاتصال. على سبيل المثال:Kotlin
// Build the parameters to enable the Billing Choice program and assign the listener // to handle user selection of the developer-provided billing option. val params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperProvidedBillingListener(developerProvidedBillingListener) .build() // Build the parameters to enable support for pending purchases. val pendingPurchasesParams = PendingPurchasesParams.newBuilder() .enableOneTimeProducts() .build() // Construct the BillingClient instance with the purchases updated listener, // pending purchases support, and the billing choice params. val billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases(pendingPurchasesParams) .enableBillingProgram(params) .build() // Establish a connection to Google Play val billingResult = suspendCancellableCoroutine{ continuation -> billingClient.startConnection(object : BillingClientStateListener { // Called when the connection setup process completes. override fun onBillingSetupFinished(billingResult: BillingResult) { // Resume the coroutine and pass back the BillingResult to the caller. continuation.resume(billingResult) } // Called if the connection to the Play Store service is dropped. // This prevents the await or suspension point from hanging indefinitely. override fun onBillingServiceDisconnected() { continuation.resume( BillingResult.newBuilder() .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) .setDebugMessage("Billing service disconnected during connection setup") .build() ) } }) } Java
EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperProvidedBillingListener(developerProvidedBillingListener) .build(); BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases( PendingPurchasesParams.newBuilder() .enableOneTimeProducts() .build() ) .enableBillingProgram(params) .build();تأكَّد من أنّ خيار الفوترة الذي تعرضه Google متاح للمستخدم.
استدعِ الدالة
isBillingProgramAvailableAsyncللتحقّق من مدى توفّر البرنامج ، ثم استدعِ الدالةqueryProductDetailsAsyncلعرض المنتجات المتاحة. على سبيل المثال:Kotlin
val (billingResult, billingProgramAvailabilityDetails) = billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE) if (billingResult.responseCode == BillingResponseCode.OK) { val billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.GOOGLE_RENDERED ) { // Billing choice is available. Query products and proceed. } else { // Fallback to other available programs. } } else { // Fallback to other available programs. }Java
// ... billingClient.isBillingProgramAvailableAsync( BillingProgram.BILLING_CHOICE, (billingResult, billingProgramAvailabilityDetails) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK) { BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails(); if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.GOOGLE_RENDERED) { // Billing choice is available. Query products and proceed. } else { // Fallback to other available programs. } } else { // Fallback to other available programs. } } );ملاحظة:
billingProgramAvailabilityDetailsتخبرك ما إذا كانت شاشة خيار الفوترة التي تعرضها Google أو التي يعرضها المطوّر متاحة.استدعِ الدالة
launchBillingFlowلبدء مسار الشراء عندما ينقر المستخدم على "شراء". إذا كان خيار الفوترة متاحًا، مرِّرDeveloperBillingOptionParamsإلىBillingFlowParams. على سبيل المثال:Kotlin
val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build() val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .enableDeveloperBillingOption(developerBillingOptionParams) .build() val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)Java
DeveloperBillingOptionParams developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build(); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .enableDeveloperBillingOption(developerBillingOptionParams) .build(); BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);ملاحظة: يتم عرض أدوات رقابة الأهل للمستخدمين الخاضعين للإشراف.
تعامَل مع خيار المستخدم لنوع الفوترة على النحو التالي:
- إذا اختار المستخدم "الفوترة في Play"، يتم عرض نتيجة الفوترة على الـ
PurchasesUpdatedListenerالمسجَّلة في الخطوة 1. - إذا اختار المستخدم نظام الفوترة البديل، يتم عرض نتيجة الفوترة على
DeveloperProvidedBillingListenerالمسجَّلة في الخطوة 1. يحتويDeveloperProvidedBillingDetailsالمعروض في هذه الحالة علىexternalTransactionToken. سيتم استخدام الرمز لإعداد تقارير المعاملات.
- إذا اختار المستخدم "الفوترة في Play"، يتم عرض نتيجة الفوترة على الـ
التعامل مع السيناريو 1ب
يعرض المطوّر شاشة الخيار وتتم معالجة نظام الفوترة البديل داخل تطبيقك. اتّبِع الخطوات التالية لتفعيل خيار الفوترة في هذا السيناريو:
استدعِ الدالة
enableBillingProgramبدون الـDeveloperProvidedBillingListenerفيEnableBillingProgramParamsعند إنشاء مثيلBillingClient، ثم ابدأ الاتصال. على سبيل المثال:Kotlin
// Build the parameters to enable the Billing Choice program. val params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build() // Construct the BillingClient instance with the purchases updated listener, // pending purchases support, and the billing choice params. val billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableBillingProgram(params) .build() // Establish a connection to Google Play val billingResult = suspendCancellableCoroutine<BillingResult> { continuation -> billingClient.startConnection(object : BillingClientStateListener { // Called when the connection setup process completes. override fun onBillingSetupFinished(billingResult: BillingResult) { // Resume the coroutine and pass back the BillingResult to the caller. continuation.resume(billingResult) } // Called if the connection to the Play Store service is dropped. // This prevents the await or suspension point from hanging indefinitely. override fun onBillingServiceDisconnected() { continuation.resume( BillingResult.newBuilder() .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) .setDebugMessage("Billing service disconnected during connection setup") .build() ) } }) }Java
EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build(); BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableBillingProgram(params) .build();تأكَّد من أنّ خيار الفوترة الذي يعرضه المطوّر متاح للمستخدم.
استدعِ الدالة
isBillingProgramAvailableAsyncللتحقّق من مدى توفّر البرنامج ، ثم استدعِ الدالةqueryProductDetailsAsyncلعرض المنتجات المتاحة. على سبيل المثال:Kotlin
val (billingResult, billingProgramAvailabilityDetails) = billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE) if (billingResult.responseCode == BillingResponseCode.OK) { val billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.DEVELOPER_RENDERED ) { // Billing choice is available. Query products and proceed. // You can inspect details such as: // - billingChoiceAvailabilityDetails.choiceScreenType // - billingChoiceAvailabilityDetails.isExternalLinkAvailable } else { // Fallback to other available programs. } } else { // Fallback to other available programs. }Java
// ... billingClient.isBillingProgramAvailableAsync( BillingProgram.BILLING_CHOICE, (billingResult, billingProgramAvailabilityDetails) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK) { BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails(); if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.DEVELOPER_RENDERED) { // Billing choice is available. Query products and proceed. // You can inspect details such as: // - billingChoiceAvailabilityDetails.getChoiceScreenType() // - billingChoiceAvailabilityDetails.isExternalLinkAvailable() } else { // Fallback to other available programs. } } else { // Fallback to other available programs. } } );ملاحظة:
billingProgramAvailabilityDetailsتخبرك ما إذا كانت شاشة خيار الفوترة التي تعرضها Google أو التي يعرضها المطوّر متاحة.استدعِ الطريقة
getBillingChoiceInfoAsyncللحصول على بانر "الفوترة في Play" ومعلومات برنامج الولاء. على سبيل المثال:Kotlin
// 1. Create the params required for the request val params = GetBillingChoiceInfoParams.newBuilder() .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE) .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE) .build() // 2. Call the suspend method on your billingClient instance val (billingResult, playBillingChoiceInfo) = billingClient.getBillingChoiceInfo(params) if (billingResult.responseCode == BillingResponseCode.OK && playBillingChoiceInfo != null) { // Access the URL of the image associated with the Play Billing Choice val imageUrl = playBillingChoiceInfo.playBillingChoiceImageUrl // Access the Play Loyalty string information, if available val loyaltyInfo = playBillingChoiceInfo.playBillingLoyaltyInfo // Populate your developer-rendered UI elements playBillingLoyaltyTextView.text = loyaltyInfo loadImage(imageUrl, playBillingImageView) } else { // Handle error scenarios }Java
// 1. Create the params required for the request GetBillingChoiceInfoParams params = GetBillingChoiceInfoParams.newBuilder() .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE) .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE) .build(); // 2. Call the method asynchronously on your billingClient instance billingClient.getBillingChoiceInfoAsync(params, (billingResult, playBillingChoiceInfo) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK && playBillingChoiceInfo != null) { // Access the URL of the image associated with the Play Billing Choice String imageUrl = playBillingChoiceInfo.getPlayBillingChoiceImageUrl(); // Access the Play Loyalty string information, if available String loyaltyInfo = playBillingChoiceInfo.getPlayBillingLoyaltyInfo(); // Populate your developer-rendered UI elements playBillingLoyaltyTextView.setText(loyaltyInfo); loadImage(imageUrl, playBillingImageView); } else { // Handle error scenarios } });أنشِئ رمز معاملة خارجيًا مع ضبط `DeveloperBillingType` على IN_APP. على سبيل المثال:
Kotlin
// Build the parameters specifying the billing program and that the billing type is IN_APP. val params = BillingProgramReportingDetailsParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperBillingType(DeveloperBillingType.IN_APP) .build() // Call the suspending extension function to request the reporting details val (billingResult, billingProgramReportingDetails) = billingClient.createBillingProgramReportingDetails(params) if (billingResult.responseCode != BillingResponseCode.OK) { // Handle failures such as retrying due to network errors. return } // Extract the transaction token from the returned reporting details val transactionToken = billingProgramReportingDetails?.externalTransactionToken // Persist the external transaction token locally. Pass it to // DeveloperBillingOptionParams when launchBillingFlow is called. // It can also be used as part of your external websiteJava
BillingProgramReportingDetailsParams params = BillingProgramReportingDetailsParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperBillingType(DeveloperBillingType.IN_APP) .build(); billingClient.createBillingProgramReportingDetailsAsync( params, new BillingProgramReportingDetailsListener() { @Override public void onCreateBillingProgramReportingDetailsResponse( BillingResult billingResult, @Nullable BillingProgramReportingDetails billingProgramReportingDetails ) { if (billingResult.getResponseCode() != BillingResponseCode.OK) { // Handle failures such as retrying due to network errors. return; } String transactionToken = billingProgramReportingDetails.getExternalTransactionToken(); // Persist the external transaction token locally. Pass it to // DeveloperBillingOptionParams when launchBillingFlow is called. // It can also be used as part of your external website } } );عندما ينقر المستخدم على "شراء"، استدعِ الدالة
showBillingProgramInformationDialogلعرض مربّع حوار معلومات. على سبيل المثال، اطّلِع على مربّع حوار المعلومات للمستخدمين. يجب ضبط BillingProgram وtransactionToken من الخطوة 4 في الطلب.ملاحظة: يتم عرض أدوات رقابة الأهل للمستخدمين الخاضعين للإشراف.
ابدأ شاشة خيار الفوترة البديلة إذا كانت النتيجة من الخطوة السابقة هي
OK.تعامَل مع خيار المستخدم لنوع الفوترة على النحو التالي:
- إذا اختار المستخدم "الفوترة في Play"، استدعِ الدالة
launchBillingFlowباتّباع الإرشادات العادية لخدمة "الفوترة في Play". يتم عرض نتيجة الفوترة علىPurchasesUpdatedListenerالمسجَّلة في الخطوة 1. - إذا اختار المستخدم نظام الفوترة البديل، عليك معالجة المعاملة بنفسك وإبلاغ Play بها باستخدام الرمز الذي تم إنشاؤه في الخطوة 4.
- إذا اختار المستخدم "الفوترة في Play"، استدعِ الدالة
التعامل مع السيناريو 2أ
تعرض Google شاشة الخيار وتتم معالجة نظام الفوترة البديل خارج تطبيقك. اتّبِع الخطوات التالية لتفعيل خيار الفوترة في هذا السيناريو:
استدعِ الدالة
enableBillingProgramمعEnableBillingProgramParamsعند إنشاء مثيل `BillingClient`، ثم ابدأ الاتصال. على سبيل المثال:Kotlin
// Build the parameters to enable the Billing Choice program and assign the listener // to handle user selection of the developer-provided billing option. val params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperProvidedBillingListener(developerProvidedBillingListener) .build() // Build the parameters to enable support for pending purchases. val pendingPurchasesParams = PendingPurchasesParams.newBuilder() .enableOneTimeProducts() .build() // Construct the BillingClient instance with the purchases updated listener, // pending purchases support, and the billing choice params. val billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases(pendingPurchasesParams) .enableBillingProgram(params) .build() // Establish a connection to Google Play val billingResult = suspendCancellableCoroutine{ continuation -> billingClient.startConnection(object : BillingClientStateListener { // Called when the connection setup process completes. override fun onBillingSetupFinished(billingResult: BillingResult) { // Resume the coroutine and pass back the BillingResult to the caller. continuation.resume(billingResult) } // Called if the connection to the Play Store service is dropped. // This prevents the await or suspension point from hanging indefinitely. override fun onBillingServiceDisconnected() { continuation.resume( BillingResult.newBuilder() .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) .setDebugMessage("Billing service disconnected during connection setup") .build() ) } }) } Java
EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperProvidedBillingListener(developerProvidedBillingListener) .build(); BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases( PendingPurchasesParams.newBuilder() .enableOneTimeProducts() .build() ) .enableBillingProgram(params) .build();تأكَّد من توفّر ما يلي:
- خيار الفوترة الذي تعرضه Google
- رابط ويب خارجي
استدعِ الدالة
isBillingProgramAvailableAsyncللتحقّق من مدى توفّر البرنامج ، ثم استدعِ الدالةqueryProductDetailsAsyncلعرض المنتجات المتاحة. على سبيل المثال:Kotlin
// Check the availability of the billing choice program asynchronously using coroutines val (billingResult, billingProgramAvailabilityDetails) = billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE) // Ensure the billing program query succeeded if (billingResult.responseCode == BillingResponseCode.OK) { // Retrieve the availability details specific to the billing choice program val billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails // Check if billing choice is available, renders via Google Play, and external link is supported if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.GOOGLE_RENDERED && billingChoiceAvailabilityDetails.isExternalLinkAvailable ) { // Billing choice is available and external transaction links are supported. Query products and proceed. } else { // Fallback to other available programs. } } else { // Fallback to other available programs. }Java
// ... billingClient.isBillingProgramAvailableAsync( BillingProgram.BILLING_CHOICE, (billingResult, billingProgramAvailabilityDetails) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK) { BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails(); if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.GOOGLE_RENDERED && billingChoiceAvailabilityDetails.isExternalLinkAvailable()) { // Billing choice is available and external transaction links are supported. // Query products and proceed. } else { // Fallback to other available programs. } } else { // Fallback to other available programs. } } );استدعِ الدالة
createBillingProgramReportingDetailsAsyncلإنشاء رمز معاملة خارجي عندما يُظهر المستخدم نيّته في الشراء. على سبيل المثال:Kotlin
// Build the parameters for creating reporting details val params = BillingProgramReportingDetailsParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK) .build() // Call the suspend function to create billing program reporting details val (billingResult, billingProgramReportingDetails) = billingClient.createBillingProgramReportingDetails(params) // Handle response failure cases if (billingResult.responseCode != BillingResponseCode.OK) { // Handle failures such as retrying due to network errors. return } // Retrieve the external transaction token val transactionToken = billingProgramReportingDetails?.externalTransactionToken // Persist the external transaction token locally. Pass it to // DeveloperBillingOptionParams when launchBillingFlow is called. // It can also be used as part of your external websiteJava
BillingProgramReportingDetailsParams params = BillingProgramReportingDetailsParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK) .build(); billingClient.createBillingProgramReportingDetailsAsync( params, new BillingProgramReportingDetailsListener() { @Override public void onCreateBillingProgramReportingDetailsResponse( BillingResult billingResult, @Nullable BillingProgramReportingDetails billingProgramReportingDetails ) { if (billingResult.getResponseCode() != BillingResponseCode.OK) { // Handle failures such as retrying due to network errors. return; } String transactionToken = billingProgramReportingDetails.getExternalTransactionToken(); // Persist the external transaction token locally. Pass it to // DeveloperBillingOptionParams when launchBillingFlow is called. // It can also be used as part of your external website. } } );استدعِ الدالة
launchBillingFlowلبدء مسار الشراء عندما ينقر المستخدم على "شراء". إذا كان خيار الفوترة متاحًا للمستخدم، نفِّذ ما يلي:- مرِّر DeveloperBillingOptionParams إلى
BillingFlowParams. - مرِّر رمز المعاملة الخارجية من الخطوة 3 إلى
DeveloperBillingOptionParams.
على سبيل المثال:
Kotlin
// Build the developer billing option parameters with the external link URI, // the transaction token, and browser/app launch mode. val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setLinkUri(Uri.parse("https://www.example.com/external/purchase")) .setExternalTransactionToken(transactionToken) .setLaunchMode( DeveloperBillingOptionParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP ) .build()Java
DeveloperBillingOptionParams developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setLinkUri(Uri.parse("https://www.example.com/external/purchase")) .setExternalTransactionToken(transactionToken) .setLaunchMode( DeveloperBillingOptionParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP) .build();ملاحظة: يتم عرض أدوات رقابة الأهل للمستخدمين الخاضعين للإشراف.
- مرِّر DeveloperBillingOptionParams إلى
تعامَل مع خيار المستخدم لنوع الفوترة على النحو التالي:
- إذا اختار المستخدم "الفوترة في Play"، استدعِ الدالة
launchBillingFlowباتّباع الإرشادات العادية لخدمة "الفوترة في Play". يتم عرض نتيجة الفوترة علىPurchasesUpdatedListenerالمسجَّلة في الخطوة 1. - إذا اختار المستخدم نظام الفوترة البديل، يتم عرض نتيجة الفوترة على
DeveloperProvidedBillingListenerالمسجَّلة في الخطوة- يحتوي
DeveloperProvidedBillingDetailsالمعروض علىexternalTransactionTokenالذي تم تمريره إلىDeveloperBillingOptionParamsفي الخطوة 4 إذا كان رمزًا صالحًا.
- يحتوي
- إذا اختار المستخدم "الفوترة في Play"، استدعِ الدالة
التعامل مع السيناريو 2ب
يعرض المطوّر شاشة الخيار وتتم معالجة نظام الفوترة البديل خارج التطبيق. اتّبِع الخطوات التالية لتفعيل خيار الفوترة في هذا السيناريو:
استدعِ الدالة
enableBillingProgramبدون الـDeveloperProvidedBillingListenerفيEnableBillingProgramParamsعند إنشاء مثيل BillingClient ، ثم ابدأ الاتصال. على سبيل المثال:Kotlin
// Build the parameters to enable the Billing Choice program. val params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build() // Construct the BillingClient instance with the purchases updated listener, // pending purchases support, and the billing choice params. val billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableBillingProgram(params) .build() // Establish a connection to Google Play val billingResult = suspendCancellableCoroutine<BillingResult> { continuation -> billingClient.startConnection(object : BillingClientStateListener { // Called when the connection setup process completes. override fun onBillingSetupFinished(billingResult: BillingResult) { // Resume the coroutine and pass back the BillingResult to the caller. continuation.resume(billingResult) } // Called if the connection to the Play Store service is dropped. // This prevents the await or suspension point from hanging indefinitely. override fun onBillingServiceDisconnected() { continuation.resume( BillingResult.newBuilder() .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) .setDebugMessage("Billing service disconnected during connection setup") .build() ) } }) }Java
EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build(); BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableBillingProgram(params) .build();تأكَّد من توفّر ما يلي:
- خيار الفوترة الذي تعرضه Google
- رابط ويب خارجي
استدعِ الدالة
isBillingProgramAvailableAsyncللتحقّق من مدى توفّر البرنامج ، ثم استدعِ الدالةqueryProductDetailsAsyncلعرض المنتجات المتاحة. على سبيل المثال:Kotlin
// Check the availability of the billing choice program asynchronously using a coroutine val (billingResult, billingProgramAvailabilityDetails) = billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE) // Ensure the response code is OK if (billingResult.responseCode == BillingResponseCode.OK) { // Retrieve the billing choice availability details val billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails // Check if billing choice details are available, choice screen is developer-rendered, // and external transaction links are supported. if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.DEVELOPER_RENDERED && billingChoiceAvailabilityDetails.isExternalLinkAvailable ) { // Billing choice is available and external transaction links are supported. // Query products and proceed. } else { // Fallback to other available programs. } } else { // Fallback to other available programs. }Java
// ... billingClient.isBillingProgramAvailableAsync( BillingProgram.BILLING_CHOICE, (billingResult, billingProgramAvailabilityDetails) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK) { BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails(); if (billingChoiceAvailabilityDetails != null && billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.DEVELOPER_RENDERED && billingChoiceAvailabilityDetails.isExternalLinkAvailable()) { // Billing choice is available and external transaction links are supported. Query products and proceed. } else { // Fallback to other available programs. } } else { // Fallback to other available programs. } } );استدعِ الطريقة
getBillingChoiceInfoAsyncللحصول على بانر "الفوترة في Play" ومعلومات برنامج الولاء.Kotlin
// 1. Create the params required for the request val params = GetBillingChoiceInfoParams.newBuilder() .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE) .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE) .build() // 2. Call the suspend method on your billingClient instance val (billingResult, playBillingChoiceInfo) = billingClient.getBillingChoiceInfo(params) if (billingResult.responseCode == BillingResponseCode.OK && playBillingChoiceInfo != null) { // Access the URL of the image associated with the Play Billing Choice val imageUrl = playBillingChoiceInfo.playBillingChoiceImageUrl // Access the Play Loyalty string information, if available val loyaltyInfo = playBillingChoiceInfo.playBillingLoyaltyInfo // Populate your developer-rendered UI elements playBillingLoyaltyTextView.text = loyaltyInfo loadImage(imageUrl, playBillingImageView) } else { // Handle error scenarios }Java
// 1. Create the params required for the request GetBillingChoiceInfoParams params = GetBillingChoiceInfoParams.newBuilder() .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE) .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE) .build(); // 2. Call the method asynchronously on your billingClient instance billingClient.getBillingChoiceInfoAsync(params, (billingResult, playBillingChoiceInfo) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK && playBillingChoiceInfo != null) { // Access the URL of the image associated with the Play Billing Choice String imageUrl = playBillingChoiceInfo.getPlayBillingChoiceImageUrl(); // Access the Play Loyalty string information, if available String loyaltyInfo = playBillingChoiceInfo.getPlayBillingLoyaltyInfo(); // Populate your developer-rendered UI elements playBillingLoyaltyTextView.setText(loyaltyInfo); loadImage(imageUrl, playBillingImageView); } else { // Handle error scenarios } });استدعِ الدالة
createBillingProgramReportingDetailsAsyncلإنشاء رمز معاملة خارجي عندما يُظهر المستخدم نيّته في الشراء. على سبيل المثال:Kotlin
// Build the parameters for creating reporting details val params = BillingProgramReportingDetailsParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK) .build() // Call the suspend function to create billing program reporting details val (billingResult, billingProgramReportingDetails) = billingClient.createBillingProgramReportingDetails(params) // Handle response failure cases if (billingResult.responseCode != BillingResponseCode.OK) { // Handle failures such as retrying due to network errors. return } // Retrieve the external transaction token val transactionToken = billingProgramReportingDetails?.externalTransactionToken // Persist the external transaction token locally. Pass it to // DeveloperBillingOptionParams when launchBillingFlow is called. // It can also be used as part of your external websiteJava
BillingProgramReportingDetailsParams params = BillingProgramReportingDetailsParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK) .build(); billingClient.createBillingProgramReportingDetailsAsync( params, new BillingProgramReportingDetailsListener() { @Override public void onCreateBillingProgramReportingDetailsResponse( BillingResult billingResult, @Nullable BillingProgramReportingDetails billingProgramReportingDetails ) { if (billingResult.getResponseCode() != BillingResponseCode.OK) { // Handle failures such as retrying due to network errors. return; } String transactionToken = billingProgramReportingDetails.getExternalTransactionToken(); // Persist the external transaction token locally. Pass it to // DeveloperBillingOptionParams when launchBillingFlow is called. // It can also be used as part of your external website. } } );ابدأ شاشة الخيار البديلة عندما ينقر المستخدم على "شراء".
تعامَل مع خيار المستخدم لنوع الفوترة على النحو التالي:
إذا اختار المستخدم "الفوترة في Play"، استدعِ الدالة
launchBillingFlowباتّباع الإرشادات العادية لخدمة "الفوترة في Play". يتم عرض نتيجة الفوترة علىPurchasesUpdatedListenerالمسجَّلة في الخطوة 1.يتم عرض أدوات رقابة الأهل للمستخدمين الخاضعين للإشراف.
إذا اختار المستخدم نظام الفوترة البديل، استدعِ الدالة launchExternalLink. على سبيل المثال:
Kotlin
// An activity reference from which the purchase flow will be launched. val activity: Activity = ... val params = LaunchExternalLinkParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) // You can pass along the external transaction token from // BillingProgramReportingDetails as a URL parameter in the URI .setLinkUri(yourLinkUri) .setLinkType(LaunchExternalLinkParams.LinkType.LINK_TO_DIGITAL_CONTENT_OFFER) .setLaunchMode( LaunchExternalLinkParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP ) .build() // Call launchExternalLink with a callback billingClient.launchExternalLink(activity, params) { billingResult -> if (billingResult.responseCode == BillingResponseCode.OK) { // Proceed with the rest of the purchase flow. If the user // purchases an item, be sure to report the transaction to Google // Play. } else { // Handle failures such as retrying due to network errors. } }Java
// An activity reference from which the purchase flow will be launched. Activity activity = ...; LaunchExternalLinkParams params = LaunchExternalLinkParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) // You can pass along the external transaction token from // BillingProgramReportingDetails as a URL parameter in the URI .setLinkUri(yourLinkUri) .setLinkType(LaunchExternalLinkParams.LinkType.LINK_TO_DIGITAL_CONTENT_OFFER) .setLaunchMode( LaunchExternalLinkParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP) .setExternalTransactionToken(transactionToken) .build(); LaunchExternalLinkResponseListener listener = new LaunchExternalLinkResponseListener() { @Override public void onLaunchExternalLinkResponse(BillingResult billingResult) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // Proceed with the rest of the purchase flow. If the user // purchases an item, be sure to report the transaction to Google // Play. } else { // Handle failures such as retrying due to network errors. } } }; billingClient.launchExternalLink(activity, params, listener);مرِّر رمز المعاملة الخارجية من الخطوة 4 إلى
LaunchExternalLinkParams. إذا عرضت الدالة OK، تابع المعاملة وأبلِغ Google Play بها.
خيار الفوترة أثناء استبدال الاشتراك
عند استبدال الاشتراك، يجب عدم عرض شاشة خيار المستخدم لأنّ خيار المستخدم لعملية الشراء الأصلية يتم الاحتفاظ به للترقيات والرجوع إلى إصدار سابق.
إذا تمت معالجة عملية الشراء الأصلية من خلال "الفوترة في Google Play"، عليك
استدعاء launchBillingFlow باستخدام معلومات استبدال الاشتراك العادية في "الفوترة في Google Play".
ومع ذلك، إذا تمت معالجة عملية الشراء الأصلية من خلال نظام فوترة بديل ، تختلف معالجة عمليات استبدال الاشتراك قليلاً استنادًا إلى السيناريوهات.
استبدال الاشتراك في السيناريو 1أ
على المستخدمين الذين يطلبون ترقية أو الرجوع إلى إصدار سابق المتابعة من خلال نظام الفوترة البديل الذي يوفّره المطوّر بدون المرور بتجربة خيار المستخدم مرة أخرى.
لإجراء ذلك، استدعِ الدالة launchBillingFlow عندما يطلب المستخدم ترقية أو الرجوع إلى إصدار سابق. استخدِم setOriginalExternalTransactionId داخل العنصر SubscriptionUpdateParams في المَعلمات لتوفير رقم تعريف المعاملة الخارجية لعملية الشراء الأصلية. لا يؤدي ذلك إلى عرض شاشة خيار المستخدم، لأنّ خيار المستخدم لعملية الشراء الأصلية يتم الاحتفاظ به للترقيات والرجوع إلى إصدار سابق. يؤدي استدعاء launchBillingFlow في هذه الحالة
إلى إنشاء رمز معاملة خارجي جديد للمعاملة يمكنك
استرداده من معاودة الاتصال.
Kotlin
// The external transaction ID from the current
// alternative billing subscription.
val externalTransactionId = //... ;
val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder()
.setBillingProgram(BillingProgram.BILLING_CHOICE)
.build()
val billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
// Fetched using queryProductDetailsAsync.
.setProductDetails(productDetailsNewPlan)
// offerIdToken can be found in
// ProductDetails=>SubscriptionOfferDetails.
.setOfferToken(offerTokenNewPlan)
.build()
)
)
.setSubscriptionUpdateParams(
BillingFlowParams.SubscriptionUpdateParams.newBuilder()
.setOriginalExternalTransactionId(externalTransactionId)
.build()
)
.enableDeveloperBillingOption(developerBillingOptionParams)
.build()
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
// When the user selects the alternative billing flow,
// the DeveloperProvidedBillingListener is triggered.
Java
// The external transaction ID from the current
// alternative billing subscription.
String externalTransactionId = //... ;
DeveloperBillingOptionParams developerBillingOptionParams =
DeveloperBillingOptionParams.newBuilder()
.setBillingProgram(BillingProgram.BILLING_CHOICE)
.build();
List<ProductDetailsParams> productDetailsParamsList = new ArrayList<>();
productDetailsParamsList.add(
ProductDetailsParams.newBuilder()
// Fetched using queryProductDetailsAsync.
.setProductDetails(productDetailsNewPlan)
// offerIdToken can be found in
// ProductDetails=>SubscriptionOfferDetails
.setOfferToken(offerTokenNewPlan)
.build());
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder()
.setOriginalExternalTransactionId(externalTransactionId)
.build())
.enableDeveloperBillingOption(developerBillingOptionParams)
.build();
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
// When the user selects the alternative billing flow,
// the DeveloperProvidedBillingListener is triggered.
استبدال الاشتراك في السيناريو 1ب
في هذا السيناريو، يجب إنشاء رمز معاملة خارجي جديد. الفرق الوحيد عن عملية الشراء العادية هو أنّه في هذا السيناريو، يتم الاحتفاظ بخيار المستخدم، وليس عليك عرض شاشة الخيار للترقية أو الرجوع إلى إصدار سابق. ومع ذلك، عليك عرض مربّع حوار المعلومات الذي يظهر مرة واحدة وإقرار الأهل.
للاطّلاع على نموذج رمز التكامل، راجِع الخطوة 4 في السيناريو 1ب: يعرض المطوّر شاشة الخيار و تتم معالجة نظام الفوترة البديل داخل تطبيقك.
استبدال الاشتراك في السيناريو 2أ
بالنسبة إلى الاشتراكات التي تم شراؤها في الأصل من خلال الموقع الإلكتروني للمطوّر أو تطبيق دفع بعد خيار المستخدم، على المستخدمين الذين يطلبون ترقية أو الرجوع إلى إصدار سابق المتابعة من خلال الموقع الإلكتروني للمطوّر أو تطبيق دفع بدون المرور بتجربة خيار المستخدم مرة أخرى.
لإجراء ذلك، استدعِ الدالة launchBillingFlow عندما يطلب المستخدم ترقية أو الرجوع إلى إصدار سابق. بدلاً من تحديد مَعلمات أخرى ضمن العنصر
SubscriptionUpdateParams، استخدِم setOriginalExternalTransactionId،
مع توفير رقم تعريف المعاملة الخارجية لعملية الشراء الأصلية.
يجب أيضًا توفير DeveloperBillingOptionParams في هذا الاستدعاء. لا يؤدي ذلك إلى عرض شاشة خيار المستخدم، لأنّ خيار المستخدم لعملية الشراء الأصلية يتم الاحتفاظ به للترقيات والرجوع إلى إصدار سابق. على سبيل المثال:
Kotlin
val externalTransactionId = //... ;
// 1. Construct DeveloperBillingOptionParams indicating the billing program
val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder()
.setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
.build()
// 2. Build BillingFlowParams combining DeveloperBillingOptionParams and SubscriptionUpdateParams
val billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
// Fetched using queryProductDetailsAsync.
.setProductDetails(productDetailsNewPlan)
// offerIdToken can be found in ProductDetails=>SubscriptionOfferDetails.
.setOfferToken(offerTokenNewPlan)
.build()
)
)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder()
.setOriginalExternalTransactionId(externalTransactionId)
.build()
)
.enableDeveloperBillingOption(developerBillingOptionParams)
.build()
Java
String externalTransactionId = //... ;
// 1. Construct DeveloperBillingOptionParams indicating the billing program
DeveloperBillingOptionParams developerBillingOptionParams =
DeveloperBillingOptionParams.newBuilder()
.setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
.build();
// 2. Add ProductDetailsParams
List productDetailsParamsList = new ArrayList<>();
productDetailsParamsList.add(
ProductDetailsParams.newBuilder()
// Fetched using queryProductDetailsAsync.
.setProductDetails(productDetailsNewPlan)
// offerIdToken can be found in ProductDetails=>SubscriptionOfferDetails
.setOfferToken(offerTokenNewPlan)
.build());
// 3. Build BillingFlowParams combining DeveloperBillingOptionParams and SubscriptionUpdateParams
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder()
.setOriginalExternalTransactionId(externalTransactionId)
.build())
.enableDeveloperBillingOption(developerBillingOptionParams)
.build();
عليك أيضًا إنشاء رمز معاملة خارجي جديد. على سبيل المثال:
Kotlin
val params =
BillingProgramReportingDetailsParams.newBuilder()
.setBillingProgram(BillingProgram.BILLING_CHOICE)
.setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
.build()
billingClient.createBillingProgramReportingDetailsAsync(
params,
object : BillingProgramReportingDetailsListener {
override fun onCreateBillingProgramReportingDetailsResponse(
billingResult: BillingResult,
billingProgramReportingDetails: BillingProgramReportingDetails?
) {
if (billingResult.responseCode != BillingResponseCode.OK) {
// Handle failures such as retrying due to network errors.
return
}
val externalTransactionToken =
billingProgramReportingDetails?.externalTransactionToken
// Persist the external transaction token locally. Pass it to
// the external website using DeveloperBillingOptionParams when
// launchBillingFlow is called.
}
}
)
Java
BillingProgramReportingDetailsParams params =
BillingProgramReportingDetailsParams.newBuilder()
.setBillingProgram(BillingProgram.BILLING_CHOICE)
.setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
.build();
billingClient.createBillingProgramReportingDetailsAsync(
params,
new BillingProgramReportingDetailsListener() {
@Override
public void onCreateBillingProgramReportingDetailsResponse(
BillingResult billingResult,
@Nullable BillingProgramReportingDetails billingProgramReportingDetails) {
if (billingResult.getResponseCode() != BillingResponseCode.OK) {
// Handle failures such as retrying due to network errors.
return;
}
String transactionToken =
billingProgramReportingDetails.getExternalTransactionToken();
// Persist the external transaction token locally. Pass it to
// the external website using DeveloperBillingOptionParams when
// launchBillingFlow is called.
}
});
بعد إنشاء الرمز الجديد، عليك استدعاء الطريقة launchBillingFlow
لبدء مسار الشراء.
استبدال الاشتراك في السيناريو 2ب
تشبه خطوات معالجة استبدال الاشتراك في هذا السيناريو الخطوات الموضّحة في استبدال الاشتراك في السيناريو 2أ. الفرق الوحيد هو أنّه بعد إنشاء رمز المعاملة، بدلاً من استدعاء الطريقة launchBillingFlow، عليك استدعاء launchExternalLink لعرض مربّع حوار إخلاء المسؤولية عن الانتقال إلى خارج التطبيق. في هذا السيناريو، يتم الاحتفاظ بخيار المستخدم، وليس عليك عرض شاشة الخيار للترقية أو الرجوع إلى إصدار سابق.
للاطّلاع على نموذج رمز التكامل، راجِع الخطوة 6 في السيناريو 2ب: يعرض المطوّر شاشة الخيار و تتم معالجة نظام الفوترة البديل داخل تطبيقك.