Google Play Uninstalla Pp I Ahve Bought Shoudl I Buy It Again
This topic describes how to integrate the Google Play Billing Library into your app to starting time selling products.
This topic includes code examples that are based on the official sample apps on GitHub. See additional resources for a consummate list of sample apps and other resources that you can use while integrating.
Life of a buy
Here's a typical purchase menses for a one-time purchase or a subscription.
- Testify the user what they tin buy.
- Launch the purchase flow for the user to accept the buy.
- Verify the buy on your server.
- Give content to the user.
- Acknowledge commitment of the content. For consumable products, consume the purchase so that the user tin can purchase the detail again.
Subscriptions automatically renew until they are canceled. A subscription can go through the following states:
- Active: User is in practiced continuing and has access to the subscription.
- Cancelled: User has cancelled but all the same has access until expiration.
- In grace period: User experienced a payment issue only still has access while Google is retrying the payment method.
- On hold: User experienced a payment issue and no longer has access while Google is retrying the payment method.
- Paused: User paused their admission and does not have access until they resume.
- Expired: User has cancelled and lost access to the subscription. The user is considered churned at expiration.
Purchase tokens and Society IDs
Google Play tracks products and transactions using purchase tokens and Order IDs.
- A purchase token is a cord that represents a buyer's entitlement to a product on Google Play. It indicates that a Google user is entitled to a specific product that is represented past a purchase object. Yous can use the purchase token with the Google Play Developer API.
- An Order ID is a string that represents a financial transaction on Google Play. This string is included in a receipt that is emailed to the heir-apparent.
Order IDs are created every time a financial transaction occurs. Purchase tokens are generated only when a user completes a buy menses.
- For one-fourth dimension products, every purchase creates a new purchase token. Most purchases too generate a new Order ID. The exception to this is when the user is not charged whatever money, as described in Promo codes.
- For subscriptions, an initial purchase creates a purchase token and an Order ID. For each automatic renewal, the purchase token stays the same, and a new Order ID is issued. Upgrades, downgrades, replacements, and re-sign-ups all create new purchase tokens and Society IDs.
For subscriptions, notation the following:
- Subscription upgrades, downgrades, and other subscription buy flows generate buy tokens that must supercede a previous purchase token. You lot must invalidate the purchase tokens that appear in the
linkedPurchaseTokenfield of the Google Play Developer API. For more information, see Implementing linkedPurchaseToken correctly to foreclose indistinguishable subscriptions. - Order numbers for subscription renewals contain an additional integer that represents a specific renewal instance. For example, an initial subscription Order ID might be
GPA.1234-5678-9012-34567with subsequent Society IDs beingGPA.1234-5678-9012-34567..0(first renewal),GPA.1234-5678-9012-34567..1(2nd renewal), and and then on.
Error Handling
The Google Play Billing Library returns errors in the form of BillingResult. A BillingResult contains a BillingResponseCode, which categorizes possible billing-related errors that your app tin can encounter. For example, if you receive a SERVICE_DISCONNECTED error lawmaking, your app should reinitialize the connection with Google Play. Additionally, a BillingResult contains a debug message, which is useful during development to diagnose errors.
Initialize a connection to Google Play
The first step to integrate with Google Play'south billing arrangement is to add the Google Play Billing Library to your app and initialize a connectedness.
Add the Google Play Billing Library dependency
Add together the Google Play Billing Library dependency to your app's build.gradle file equally shown:
Groovy
dependencies { def billing_version = "5.0.0" implementation "com.android.billingclient:billing:$billing_version" } Kotlin
dependencies { val billing_version = "5.0.0" implementation("com.android.billingclient:billing:$billing_version") } If you're using Kotlin, the Google Play Billing Library KTX module contains Kotlin extensions and coroutines support that enable you to write idiomatic Kotlin when using the Google Play Billing Library. To include these extensions in your project, add the following dependency to your app's build.gradle file as shown:
Groovy
dependencies { def billing_version = "5.0.0" implementation "com.android.billingclient:billing-ktx:$billing_version" } Kotlin
dependencies { val billing_version = "v.0.0" implementation("com.android.billingclient:billing-ktx:$billing_version") } Initialize a BillingClient
Once you've added a dependency on the Google Play Billing Library, yous demand to initialize a BillingClient instance. BillingClient is the main interface for communication betwixt the Google Play Billing Library and the residuum of your app. BillingClient provides convenience methods, both synchronous and asynchronous, for many common billing operations. It's strongly recommended that you lot have i active BillingClient connection open at i time to avoid multiple PurchasesUpdatedListener callbacks for a single issue.
To create a BillingClient, employ newBuilder(). You lot can laissez passer any context to newBuilder(), and BillingClient uses information technology to get an application context. That means yous don't need to worry about retentivity leaks. To receive updates on purchases, you must besides telephone call setListener(), passing a reference to a PurchasesUpdatedListener. This listener receives updates for all purchases in your app.
Kotlin
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases -> // To be implemented in a later section. } private var billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .build() Coffee
individual PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() { @Override public void onPurchasesUpdated(BillingResult billingResult, Listing<Buy> purchases) { // To be implemented in a afterward section. } }; private BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .build(); Connect to Google Play
After you accept created a BillingClient, you need to institute a connection to Google Play.
To connect to Google Play, telephone call startConnection(). The connection process is asynchronous, and you must implement a BillingClientStateListener to receive a callback one time the setup of the client is complete and information technology's set up to brand further requests.
You must also implement retry logic to handle lost connections to Google Play. To implement retry logic, override the onBillingServiceDisconnected() callback method, and make certain that the BillingClient calls the startConnection() method to reconnect to Google Play before making further requests.
The following instance demonstrates how to offset a connection and test that information technology's ready to utilize:
Kotlin
billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingResponseCode.OK) { // The BillingClient is ready. You tin can query purchases hither. } } override fun onBillingServiceDisconnected() { // Try to restart the connection on the next asking to // Google Play by calling the startConnection() method. } }) Coffee
billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(BillingResult billingResult) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // The BillingClient is prepare. Yous can query purchases here. } } @Override public void onBillingServiceDisconnected() { // Endeavour to restart the connection on the next asking to // Google Play by calling the startConnection() method. } }); Show products bachelor to buy
After you have established a connection to Google Play, you are set up to query for your available products and brandish them to your users.
Querying for production details is an important step before displaying your products to your users, as it returns localized product data. For subscriptions, ensure your product brandish follows all Play policies.
To query for in-app product details, call queryProductDetailsAsync().
To handle the upshot of the asynchronous operation, you must likewise specify a listener which implements the ProductDetailsResponseListener interface. Yous tin can and so override onProductDetailsResponse(), which notifies the listener when the query finishes, as shown in the following instance:
Kotlin
val queryProductDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList( ImmutableList.of( Product.newBuilder() .setProductId("product_id_example") .setProductType(ProductType.SUBS) .build())) .build() billingClient.queryProductDetailsAsync( queryProductDetailsParams, ProductDetailsResponseListener { billingResult, productDetailsList -> // check billingResult // process returned productDetailsList } ) Coffee
QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList( ImmutableList.of( Product.newBuilder() .setProductId("product_id_example") .setProductType(ProductType.SUBS) .build())) .build(); billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse(BillingResult billingResult, Listing<ProductDetails> productDetailsList) () { // check BillingResult // process returned ProductDetails listing } } ) When querying for product details, pass an case of QueryProductDetailsParams that specifies a listing of production ID strings created in Google Play Console along with a ProductType. The ProductType can be either ProductType.INAPP for one-time products or ProductType.SUBS for subscriptions.
Querying with Kotlin extensions
If y'all're using Kotlin extensions, yous can query for in-app production details by calling the queryProductDetails() extension function.
queryProductDetails() leverages Kotlin coroutines then that you don't need to define a split listener. Instead, the part suspends until the querying completes, after which yous tin procedure the issue:
suspend fun processPurchases() { val productList = ArrayList<String>() productList.add("product_id_example") val params = QueryProductDetailsParams.newBuilder() params.setProductList(productList) .setType(ProductType.SUBS) // leverage queryProductDetails Kotlin extension office val productDetailsResult = withContext(Dispatchers.IO) { billingClient.queryProductDetails(params.build()) } // Process the upshot. } Process the result
The Google Play Billing Library stores the query results in a Listing of ProductDetails objects. You can then call a diverseness of methods on each ProductDetails object in the list to view relevant information about an in-app production, such as its price or clarification. To view the available product detail information, see the list of methods in the ProductDetails grade.
Before offering an detail for sale, check that the user does non already own the item. If the user has a consumable that is yet in their item library, they must eat the item before they tin buy it again.
Earlier offering a subscription, verify that the user is not already subscribed. Also note the following:
-
queryProductDetailsAsync()returns subscription product details and a maximum of 50 offers per subscription. -
queryProductDetailsAsync()returns only offers for which the user is eligible. If the user attempts to purchase an offer for which they're ineligible (for instance, if the app is displaying an outdated list of eligible offers), Play informs the user that they are ineligible, and the user tin choose to purchase the base of operations plan instead.
Launch the purchase flow
To start a purchase request from your app, telephone call the launchBillingFlow() method from your app's main thread. This method takes a reference to a BillingFlowParams object that contains the relevant ProductDetails object obtained from calling queryProductDetailsAsync(). To create a BillingFlowParams object, use the BillingFlowParams.Builder class.
Kotlin
// An activity reference from which the billing flow will be launched. val activity : Activity = ...; // Call up a value for "productDetails" by calling queryProductDetails() // extension role. val flowParams = BillingFlowParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(selectedOfferIdToken) .build() val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
Java
// An activity reference from which the billing period will be launched. Activity activity = ...; BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList( ImmuableList.of( ProductDetailsParams.newBuilder() // fetched via queryProductDetailsAsync .setProductDetails(productDetails) // to get an offer token, call ProductDetails.getOfferDetails() // for a list of offers that are bachelor to the user .setOfferToken(selectedOfferToken) .build() ) ) .build(); BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams); // procedure purchase results from PurchasesUpdatedListener registered with BillingClient public void onPurchaseUpdated(BillingResult billingResult, @Nullable Listing<Purchase> purchases) { // check BillingResult // procedure returned Purchase list, due east.one thousand. grant entitlement } The launchBillingFlow() method returns ane of several response codes listed in BillingClient.BillingResponseCode. Exist certain to check this result to ensure there were no errors launching the buy flow. A BillingResponseCode of OK indicates a successful launch.
On a successful call to launchBillingFlow(), the system displays the Google Play purchase screen. Figure i shows a purchase screen for a subscription:
Google Play calls onPurchasesUpdated() to deliver the result of the buy performance to a listener that implements the PurchasesUpdatedListener interface. The listener is specified using the setListener() method when you initialized your client.
You must implement onPurchasesUpdated() to handle possible response codes. The following example shows how to override onPurchasesUpdated():
Kotlin
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) { if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) { for (purchase in purchases) { handlePurchase(purchase) } } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) { // Handle an error caused by a user cancelling the buy menses. } else { // Handle any other mistake codes. } } Coffee
@Override void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { for (Buy buy : purchases) { handlePurchase(purchase); } } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) { // Handle an mistake caused past a user cancelling the buy flow. } else { // Handle any other error codes. } } A successful purchase generates a Google Play purchase success screen like to figure ii.
A successful purchase also generates a purchase token, which is a unique identifier that represents the user and the product ID for the in-app product they purchased. Your apps can shop the purchase token locally, though we recommend passing the token to your secure backend server where you can then verify the purchase and protect against fraud. This process is further described in the following department.
The user is as well emailed a receipt of the transaction containing an Society ID or a unique ID of the transaction. Users receive an email with a unique Club ID for each ane-time production buy, and also for the initial subscription purchase and subsequent recurring automatic renewals. You can employ the Order ID to manage refunds in the Google Play Console.
Bespeak a personalized price
If your app can be distributed to users in the European Union, use the setIsOfferPersonalized() method to disembalm to users that an particular's toll was personalized using automated decision-making.
You lot must consult Art. 6 (ane) (ea) CRD of the Consumer Rights Directive (2011/83/Eu) to determine if the toll yous are offering to users is personalized.
setIsOfferPersonalized() takes a boolean input. When true, the Play UI includes the disclosure. When false, the UI omits the disclosure. The default value is fake.
Run across the Consumer Help Centre for more information.
Processing purchases
Once a user completes a purchase, your app so needs to process that purchase. In near cases, your app is notified of purchases through your PurchasesUpdatedListener. but at that place are cases where your app is fabricated aware of calling BillingClient.queryPurchasesAsync() as described in Fetching purchases.
Your app should process a buy in the following way:
- Verify the buy.
- Give content to the user, and admit delivery of the content. Optionally, marker the item as consumed then that the user can purchase the item once more.
To verify a buy, first bank check that the purchase state is PURCHASED. If the purchase is Pending, then you should process the buy as described in Handling pending transactions. For purchases received from onPurchasesUpdated() or queryPurchasesAsync(), you should farther verify the purchase to ensure legitimacy before your app grants entitlement. To learn how to properly verify a buy, run across Verify purchases earlier granting entitlements.
One time you've verified the purchase, your app is prepare to grant entitlement to the user. After granting entitlement, your app must and then admit the purchase. This acknowledgement communicates to Google Play that yous have granted entitlement for the purchase.
The procedure to grant entitlement and acknowledge the purchase depends on whether the buy is a non-consumable, a consumable, or a subscription.
For consumables, the consumeAsync() method fulfills the acknowledgement requirement and indicates that your app has granted entitlement to the user. This method also enables your app to make the one-time product available for purchase again.
To indicate that a onetime production has been consumed, call consumeAsync() and include the purchase token that Google Play should make bachelor for repurchase. Yous must also pass an object that implements the ConsumeResponseListener interface. This object handles the result of the consumption operation. You can override the onConsumeResponse() method, which the Google Play Billing Library calls when the operation is complete.
The following example illustrates consuming a production using the associated purchase token:
Kotlin
suspend fun handlePurchase(purchase: Purchase) { // Buy retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. val purchase : Buy = ...; // Verify the buy. // Ensure entitlement was non already granted for this purchaseToken. // Grant entitlement to the user. val consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(buy.getPurchaseToken()) .build() val consumeResult = withContext(Dispatchers.IO) { client.consumePurchase(consumeParams) } } Coffee
void handlePurchase(Purchase purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. Buy purchase = ...; // Verify the buy. // Ensure entitlement was non already granted for this purchaseToken. // Grant entitlement to the user. ConsumeParams consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); ConsumeResponseListener listener = new ConsumeResponseListener() { @Override public void onConsumeResponse(BillingResult billingResult, Cord purchaseToken) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // Handle the success of the eat performance. } } }; billingClient.consumeAsync(consumeParams, listener); } To admit not-consumable purchases, utilize either BillingClient.acknowledgePurchase() from the Google Play Billing Library or Production.Purchases.Acknowledge from the Google Play Programmer API. Before acknowledging a purchase, your app should check whether it was already acknowledged past using the isAcknowledged() method in the Google Play Billing Library or the acknowledgementState field in the Google Developer API.
The following instance shows how to acknowledge a buy using the Google Play Billing Library:
Kotlin
val client: BillingClient = ... val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ... suspend fun handlePurchase() { if (purchase.purchaseState === PurchaseState.PURCHASED) { if (!purchase.isAcknowledged) { val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) val ackPurchaseResult = withContext(Dispatchers.IO) { customer.acknowledgePurchase(acknowledgePurchaseParams.build()) } } } } Coffee
BillingClient client = ... AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ... void handlePurchase(Purchase purchase) { if (purchase.getPurchaseState() == PurchaseState.PURCHASED) { if (!buy.isAcknowledged()) { AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); customer.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); } } } Subscriptions are handled similarly to non-consumables. You can acknowledge a subscription Acknowledgement using either BillingClient.acknowledgePurchase() from the Google Play Billing Library or Purchases.Subscriptions.Acknowledge from the Google Play Developer API. All initial subscription purchases demand to be acknowledged. Subscription renewals exercise non need to be acknowledged. For more than data on when subscriptions need to be acknowledged, come across the Sell subscriptions topic.
Fetching purchases
Listening to purchase updates using a PurchasesUpdatedListener is not sufficient to ensure your app processes all purchases. It's possible that your app might not be aware of all the purchases a user has made. Hither are some scenarios where your app could lose runway or exist unaware of purchases:
- Network Issues during the purchase: A user makes a successful buy and receives confirmation from Google, but their device loses network connectivity before their device receives notification of the purchase through the
PurchasesUpdatedListener. - Multiple devices: A user buys an item on one device and and then expects to see the item when they switch devices.
- Handling purchases made outside your app: Some purchases, such as promotion redemptions, tin be made outside of your app.
To handle these situations, be sure that your app calls BillingClient.queryPurchasesAsync() in your onResume() method to ensure that all purchases are successfully processed as described in processing purchases.
Handling purchases made exterior your app
Some purchases, such every bit promotion redemptions, can happen outside of your app. When a user makes a purchase outside of your app, they expect your app to show an in-app message, or employ some kind of notification mechanism to let the user know that the app correctly received and processed the purchase. Some acceptable mechanisms are:
- Show an in-app popup.
- Deliver the message to an in-app message box, and clearly stating that there is a new bulletin in the in-app message box.
- Use an Os notification message.
Keep in mind that information technology is possible for your app to exist in whatever state when your app recognizes the purchase. It is even possible for your app to not even be installed when the purchase was fabricated. Users expect to receive their purchase when they resume the app, regardless of the state in which the app is.
You must detect purchases regardless of the state in which the app is when the purchase was made. Notwithstanding, there are some exceptions where it may be acceptable to not immediately notify the user that the particular was received. For case:
- During the action part of a game, where showing a message may distract the user. In this case, you must notify the user after the action function is over.
- During cutscenes, where showing a message may distract the user. In this instance, you must notify the user after the cutscene is over.
- During the initial tutorial and user setup parts of the game. We recommend you notify new users of the reward immediately after they open the game or during initial user set. However, it is acceptable to wait until the primary game sequence is available to notify the user.
Always keep the user in mind when deciding when and how to notify your users of purchases made outside of your app. Whatever time a user doesn't immediately receive a notification, they may get dislocated, and may stop using your app, contact user back up, or complain about it on social media. Note: PurchasesUpdatedListener is registered with your application context to handle purchase updates, including purchases initiated outside of your app. This means that if your awarding process does not exist, your PurchasesUpdatedListener would not be notified. This is why your app should call BillingClient.queryPurchasesAsync() in the onResume() method every bit mentioned in Fetch Purchases.
Handling awaiting transactions
Google Play supports pending transactions, or transactions that require one or more than additional steps between when a user initiates a buy and when the payment method for the purchase is processed. Your app should not grant entitlement to these types of purchases until Google notifies you that the user's payment method was successfully charged.
For example, a user can create a Awaiting buy of an in-app item past choosing cash as their grade of payment. The user can so cull a physical store where they will complete the transaction and receive a code through both notification and electronic mail. When the user arrives at the physical store, they can redeem the lawmaking with the cashier and pay with greenbacks. Google then notifies both you and the user that cash has been received. Your app can then grant entitlement to the user.
Your app must support pending transactions by calling enablePendingPurchases() as part of initializing your app.
When your app receives a new buy, either through your PurchasesUpdatedListener or every bit a consequence of calling queryPurchasesAsync(), utilize the getPurchaseState() method to determine whether the purchase land is PURCHASED or Awaiting.
If your app is running when the user completes the purchase, your PurchasesUpdatedListener is chosen again, and the PurchaseState is at present PURCHASED. At this signal, your app can process the purchase using the standard method for processing quondam purchases. Your app should also call queryPurchasesAsync() in your app's onResume() method to handle purchases that accept transitioned to the PURCHASED state while your app was not running.
Your app can too use Real-time developer notifications with pending purchases by listening for OneTimeProductNotifications. When the purchase transitions from PENDING to PURCHASED, your app receives a ONE_TIME_PRODUCT_PURCHASED notification. If the buy is cancelled, your app receives a ONE_TIME_PRODUCT_CANCELED notification. This can happen if your customer does not complete payment in the required timeframe. When receiving these notifications, you can use the Google Play Developer API, which includes a Awaiting state for Purchases.products.
You can find detailed steps on how to exam this scenario at Exam awaiting purchases.
Handling multi-quantity purchases
Supported in versions iv.0 and higher of the Google Play Billing Library, Google Play allows customers to purchase more than than 1 of the same in-app product in one transaction past specifying a quantity from the purchase cart. Your app is expected to handle multi-quantity purchases and grant entitlement based on the specified purchase quantity.
To honor multi-quantity purchases, your app'southward provisioning logic needs to bank check for an particular quantity. You can admission a quantity field from one of the following APIs:
-
getQuantity()from the Google Play Billing Library. -
Purchases.products.quantityfrom the Google Play Developer API.
Once you've added logic to handle multi-quantity purchases, you then need to enable the multi-quantity characteristic for the corresponding product on the in-app product direction page in the Google Play Programmer Console.
Lawmaking examples
Querying for purchases
The following example shows how to query for a user's subscription purchases. Note that queryPurchasesAsync() returns merely agile subscriptions and non-consumed one-time purchases.
billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder() .setProductType(ProductType.SUBS) .build(), /* purchaseResponseListener= */ this ); // PurchaseResponseListener implementation. public void onQueryPurchasesResponse(BillingResult billingResult, Listing<Purchase> purchases) { // check BillingResult // process returned buy list, eastward.yard. display the plans user owns } Fetching purchase history
queryPurchaseHistoryAsync() returns the near recent purchase made by the user for each production, even if that purchase is expired, canceled, or consumed.
billingClient.queryPurchaseHistoryAsync( QueryPurchaseHistoryParams.newBuilder() .setProductType(ProductType.SUBS) .build(), /* purchaseHistoryResponseListener= */ this ); // PurchaseHistoryResponseListener implementation. public void onQueryPurchaseHistoryResponse( BillingResult billingResult, List<PurchaseHistoryRecord> purchasesHistoryList) { // check BillingResult // process returned purchase history list, east.g. brandish purchase history } Source: https://developer.android.com/google/play/billing/integrate
0 Response to "Google Play Uninstalla Pp I Ahve Bought Shoudl I Buy It Again"
Post a Comment