يوضّح هذا الدليل كيفية دمج تطبيقك مع واجهات برمجة التطبيقات الخاصة بـ Play Billing Library لتتمكّن من توفير خيار الفوترة للمستخدمين.
التكامل مع PBL
يمكنك دمج ميزة اختيار نظام الفوترة مع ميزة "الفوترة داخل التطبيق" في أربع سيناريوهات. تختلف السيناريوهات حسب الجهة التي تعرض شاشة الاختيار والمكان الذي سيتم فيه الدفع. يوضّح الجدول التالي سيناريوهات الدمج:
| أين تتم عملية الدفع؟ | |||
| داخل التطبيق | رابط ويب خارجي | ||
| ما هي شاشة اختيار نظام الفوترة التي تريد عرضها؟ | Google Play | السيناريو 1(أ)
يعرض Google شاشة الاختيار وتتم معالجة نظام الفوترة البديل داخل تطبيقك. |
السيناريو 1(ب)
يعرض مطوّر التطبيق شاشة الاختيار وتتم معالجة نظام الفوترة البديل داخل تطبيقك. |
| الخاصة بك (وفقًا لإرشادات تجربة المستخدم) | السيناريو 2(أ)
تعرض Google شاشة الاختيار ويتم توجيه المستخدم إلى خارج تطبيقك إلى مواقعك الإلكترونية لإجراء عمليات الشراء. |
السيناريو 2B
يعرض مطوّر التطبيق شاشة الاختيار ويتم توجيه المستخدم إلى خارج تطبيقك إلى مواقعك الإلكترونية لإجراء عمليات الشراء. |
|
يوضّح الرسم التوضيحي التالي مسار اختيار نظام الفوترة لكل من هذه السيناريوهات:
سيناريوهات دمج PBL
استنادًا إلى سيناريو الدمج، اتّبِع الخطوات الواردة في هذا القسم لتنفيذ ميزة "اختيار نظام الفوترة" في تطبيقك.
التعامل مع السيناريو 1A
تعرض 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"، سيتم عرض نتيجة الفوترة في
التعامل مع السيناريو 1B
يعرض المطوِّر شاشة اختيار نظام الفوترة، ويتم التعامل مع نظام الفوترة البديل داخل تطبيقك. اتّبِع الخطوات التالية لتفعيل خيار نظام الفوترة في هذه الحالة:
اتّصِل بـ
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"، عليك استدعاء
التعامل مع السيناريو 2A
تعرض 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 في السيناريو 1B: يعرض المطوّر شاشة اختيار نظام الفوترة البديل ويتم التعامل مع نظام الفوترة البديل داخل تطبيقك.
عند اكتمال عملية الترقية أو الرجوع إلى إصدار سابق، عليك الإبلاغ عن معاملة جديدة باستخدام رمز المعاملة الخارجية الذي تم الحصول عليه من خلال طلب الشراء السابق للاشتراك الجديد.
استبدال الاشتراك في السيناريو 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
لتشغيل مسار الشراء.
عند اكتمال عملية الترقية أو الرجوع إلى إصدار سابق، عليك الإبلاغ عن معاملة جديدة باستخدام رمز المعاملة الخارجية الذي تم الحصول عليه من خلال طلب الشراء السابق للاشتراك الجديد.
استبدال الاشتراك في السيناريو 2B
تتشابه خطوات استبدال الاشتراك في هذا السيناريو مع الخطوات الموضّحة في استبدال الاشتراك في السيناريو 2أ. الفرق الوحيد هو أنّه بعد إنشاء الرمز المميّز للمعاملة، عليك استدعاء طريقة launchExternalLink لعرض مربّع الحوار الخاص بإخلاء المسؤولية عن الانتقال إلى موقع إلكتروني خارجي، بدلاً من استدعاء طريقة launchBillingFlow. في هذه الحالة، يتم الحفاظ على اختيار المستخدم،
وليس عليك عرض شاشة الاختيار للترقية أو الرجوع إلى إصدار أقدم.
للاطّلاع على نموذج لرمز الدمج، يُرجى الانتقال إلى الخطوة 6 في السيناريو 2B: يعرض المطوّر شاشة اختيار نظام الفوترة البديل ويتم التعامل مع نظام الفوترة البديل داخل تطبيقك.
عند اكتمال عملية الترقية أو الرجوع إلى إصدار سابق، عليك الإبلاغ عن معاملة جديدة باستخدام رمز المعاملة الخارجية الذي تم الحصول عليه من خلال طلب الشراء السابق للاشتراك الجديد.