Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
RadoslawKubas authored Apr 29, 2021
2 parents 1281848 + 39cda2a commit d4b1b04
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 71 deletions.
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ Source code reference -> https://github.com/jamesmontemagno/app-ac-islandtracker
## NuGet
* NuGet: [Plugin.InAppBilling](https://www.nuget.org/packages/Plugin.InAppBilling) [![NuGet](https://img.shields.io/nuget/v/Plugin.InAppBilling.svg?label=NuGet)](https://www.nuget.org/packages/Plugin.InAppBilling/)

Dev Feed: https://ci.appveyor.com/nuget/inappbillingplugin

## Build Status
[![Build status](https://ci.appveyor.com/api/projects/status/0tfkgrlq8r2u7wb9?svg=true)](https://ci.appveyor.com/project/JamesMontemagno/inappbillingplugin)

## Platform Support

|Platform|Version|
Expand Down Expand Up @@ -76,13 +71,13 @@ https://developer.android.com/google/play/billing/integrate#pending
So, if you have a consumable... `ConsumePurchaseAsync` will also acknowledge it, if you have a non-consumable you will need to call `AcknowledgePurchaseAsync`.

## Version 3+ Linker Settings
## Version 4+ Linker Settings

For linking if you are setting **Link All** you may need to add:

#### Android:
```
Plugin.InAppBilling;Xamarin.Android.Google.BillingClient;
Plugin.InAppBilling;Xamarin.Android.Google.BillingClient
```

#### iOS:
Expand Down
9 changes: 6 additions & 3 deletions docs/CheckAndRestorePurchases.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ When users get a new device or re-install your application it is best practice t
/// Get all current purhcase for a specifiy product type.
/// </summary>
/// <param name="itemType">Type of product</param>
/// <param name="verifyPurchase">Verify purchase implementation</param>
/// <returns>The current purchases</returns>
Task<IEnumerable<InAppBillingPurchase>> GetPurchasesAsync(ItemType itemType, IInAppBillingVerifyPurchase verifyPurchase = null);
Task<IEnumerable<InAppBillingPurchase>> GetPurchasesAsync(ItemType itemType);
```

When you make a call to restore a purchase it will prompt for the user to sign in if they haven't yet, so take that into consideration.
Expand All @@ -22,7 +21,7 @@ public async Task<bool> WasItemPurchased(string productId)
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
var connected = await billing.ConnectAsync();

if (!connected)
{
Expand Down Expand Up @@ -63,6 +62,10 @@ public async Task<bool> WasItemPurchased(string productId)
}
```

## Subscriptions

On `Android` only valid on-going subscriptions will be returned. `iOS` returns all receipts for all instances of the subscripitions. Read the iOS documentation to learn more on strategies.

Learn more about `IInAppBillingVerifyPurchase` in the [Securing Purchases](SecuringPurchases.md) documentation.


Expand Down
44 changes: 19 additions & 25 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public async Task<bool> MakePurchase()
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
var connected = await billing.ConnectAsync();
if(!connected)
return false;

Expand All @@ -41,7 +41,7 @@ public async Task<bool> MakePurchase()
try
{
var billing = CrossInAppBilling.Current;
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
var connected = await billing.ConnectAsync();
if(!connected)
return false;

Expand Down Expand Up @@ -73,45 +73,39 @@ Each app store has you create them in a different area.
* Android: Go to Google Play Console -> Select App -> Store presence -> In-app products (you can only create on if you have uploaded a version of your app with this plugin or the Vending permission set).
* Microsoft: Go to Dashboard -> Select App -> Add-ons

## Permissions & Additional Setup - Version 4
## Permissions & Additional Setup

In version 4 we use Xamarin.Essentials so you must ensure you initialize it in your Android project. It is setup by default in new projects:
## iOS
iOS also has the ability to make In-App Purchases from the app store if you mark them as so. To support this you must open your `AppDelegate` and add the following to your `FinishedLaunching`:

```csharp
protected override void OnCreate(Bundle savedInstanceState) {
//...
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
//...
Plugin.InAppBilling.InAppBillingImplementation.OnShouldAddStorePayment = OnShouldAddStorePayment;
var current = Plugin.InAppBilling.CrossInAppBilling.Current; //initializes
```

That's it.

## Permissions & Additional Setup - Version 2

### Android:

The `com.android.vending.BILLING` permission is required to use In-App Billing on Android and this library will automatically added it your Android Manifest when you compile. No need to add them manually!

You must place this code in your Main/Base Activity where you will be requesting purchases from.
Then add this method in the `AppDelegate`:

```csharp
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
bool OnShouldAddStorePayment(SKPaymentQueue queue, SKPayment payment, SKProduct product)
{
base.OnActivityResult(requestCode, resultCode, data);
InAppBillingImplementation.HandleActivityResult(requestCode, resultCode, data);
//Process and check purchases
return true;
}
```

## Android Current Activity Setup
## Android

This plugin uses the [Current Activity Plugin](https://github.com/jamesmontemagno/CurrentActivityPlugin/blob/master/README.md) to get access to the current Android Activity. Be sure to complete the full setup if a MainApplication.cs file was not automatically added to your application. Please fully read through the [Current Activity Plugin Documentation](https://github.com/jamesmontemagno/CurrentActivityPlugin/blob/master/README.md). At an absolute minimum you must set the following in your Activity's OnCreate method:
In version 4 we use Xamarin.Essentials so you must ensure you initialize it in your Android project. It is setup by default in new projects:

```csharp
Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity = this;
protected override void OnCreate(Bundle savedInstanceState) {
//...
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
//...
```

It is highly recommended that you use a custom Application that are outlined in the Current Activity Plugin Documentation](https://github.com/jamesmontemagno/CurrentActivityPlugin/blob/master/README.md)
The `com.android.vending.BILLING` permission is required to use In-App Billing on Android and this library will automatically added it your Android Manifest when you compile. No need to add them manually!


<= Back to [Table of Contents](README.md)
4 changes: 2 additions & 2 deletions docs/HandlingExceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ try
}

//check purchases
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, "devId");
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase);

//possibility that a null came through.
if(purchase == null)
Expand Down Expand Up @@ -130,4 +130,4 @@ finally

```

<= Back to [Table of Contents](README.md)
<= Back to [Table of Contents](README.md)
21 changes: 3 additions & 18 deletions docs/PurchaseConsumable.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ Consumables are unique and work a bit different on each platform and the `Consum
/// </summary>
/// <param name="productId">Sku or ID of product</param>
/// <param name="itemType">Type of product being requested</param>
/// <param name="payload">Developer specific payload (can not be null)</param>
/// <param name="verifyPurchase">Verify Purchase implementation</param>
/// <returns>Purchase details</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occures during processing</exception>
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string payload, IInAppBillingVerifyPurchase verifyPurchase = null);
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, IInAppBillingVerifyPurchase verifyPurchase = null);
```

### Consume Purchase
Expand All @@ -38,22 +37,8 @@ Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, st
/// <returns>If consumed successful</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occures during processing</exception>
Task<InAppBillingPurchase> ConsumePurchaseAsync(string productId, string purchaseToken);

/// <summary>
/// Consume a purchase by trying to find purchase
/// </summary>
/// <param name="productId">Id/Sku of the product</param>
/// <param name="payload">Developer specific payload of original purchase (can not be null)</param>
/// <param name="itemType">Type of product being consumed.</param>
/// <param name="verifyPurchase">Verify Purchase implementation</param>
/// <returns>If consumed successful</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occures during processing</exception>
Task<InAppBillingPurchase> ConsumePurchaseAsync(string productId, ItemType itemType, string payload, IInAppBillingVerifyPurchase verifyPurchase = null);
```

The `payload` attribute is a special payload that is sent and then returned from the server for additional validation. It can be whatever you want it to be, but should be a constant that is used anywhere the `payload` is used.

It is recommend to use the `purchaseToken` from the original transaction. If you use the generic version with just the productId the library will do it's best to find the purchase and then consume it, but can not be guaranteed.

Example:
```csharp
Expand All @@ -62,15 +47,15 @@ public async Task<bool> PurchaseItem(string productId, string payload)
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
var connected = await billing.ConnectAsync();
if (!connected)
{
//we are offline or can't connect, don't try to purchase
return false;
}

//check purchases
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, payload);
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase);

//possibility that a null came through.
if(purchase == null)
Expand Down
12 changes: 7 additions & 5 deletions docs/PurchaseNonConsumable.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ All purchases go through the `PurchaseAsync` method and you must always `Connect
/// </summary>
/// <param name="productId">Sku or ID of product</param>
/// <param name="itemType">Type of product being requested</param>
/// <param name="payload">Developer specific payload (can not be null)</param>
/// <param name="verifyPurchase">Verify Purchase implementation</param>
/// <returns>Purchase details</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occures during processing</exception>
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string payload, IInAppBillingVerifyPurchase verifyPurchase = null);
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, IInAppBillingVerifyPurchase verifyPurchase = null);
```

The `payload` attribute is a special payload that is sent and then returned from the server for additional validation. It can be whatever you want it to be, but should be a constant that is used anywhere the `payload` is used.
On Android you must call `AcknowledgePurchaseAsync` within 3 days when a purchase is validated. Please read the [Android documentation on Pending Transactions](https://developer.android.com/google/play/billing/integrate#pending) for more information.

Example:
```csharp
Expand All @@ -31,7 +30,7 @@ public async Task<bool> PurchaseItem(string productId, string payload)
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
var connected = await billing.ConnectAsync();
if (!connected)
{
//we are offline or can't connect, don't try to purchase
Expand All @@ -49,7 +48,10 @@ public async Task<bool> PurchaseItem(string productId, string payload)
else if(purchase.State == PurchaseState.Purchased)
{
//purchased!
if(Device.RuntimePlatform == Device.Android)
{
// Must call AcknowledgePurchaseAsync else the purchase will be refunded
}
}
}
catch (InAppBillingPurchaseException purchaseEx)
Expand Down
14 changes: 9 additions & 5 deletions docs/PurchaseSubscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ All purchases go through the `PurchaseAsync` method and you must always `Connect
/// </summary>
/// <param name="productId">Sku or ID of product</param>
/// <param name="itemType">Type of product being requested</param>
/// <param name="payload">Developer specific payload (can not be null)</param>
/// <param name="verifyPurchase">Verify Purchase implementation</param>
/// <returns>Purchase details</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occures during processing</exception>
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string payload, IInAppBillingVerifyPurchase verifyPurchase = null);
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, IInAppBillingVerifyPurchase verifyPurchase = null);
```

The `payload` attribute is a special payload that is sent and then returned from the server for additional validation. It can be whatever you want it to be, but should be a constant that is used anywhere the `payload` is used.
On Android you must call `AcknowledgePurchaseAsync` within 3 days when a purchase is validated. Please read the [Android documentation on Pending Transactions](https://developer.android.com/google/play/billing/integrate#pending) for more information.

Example:
```csharp
Expand All @@ -31,15 +30,15 @@ public async Task<bool> PurchaseItem(string productId, string payload)
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.Subscription);
var connected = await billing.ConnectAsync();
if (!connected)
{
//we are offline or can't connect, don't try to purchase
return false;
}

//check purchases
var purchase = await billing.PurchaseAsync(productId, ItemType.Subscription, payload);
var purchase = await billing.PurchaseAsync(productId, ItemType.Subscription);

//possibility that a null came through.
if(purchase == null)
Expand All @@ -49,6 +48,10 @@ public async Task<bool> PurchaseItem(string productId, string payload)
else
{
//purchased!
if(Device.RuntimePlatform == Device.Android)
{
// Must call AcknowledgePurchaseAsync else the purchase will be refunded
}
}
}
catch (InAppBillingPurchaseException purchaseEx)
Expand All @@ -67,6 +70,7 @@ public async Task<bool> PurchaseItem(string productId, string payload)
}
```


Learn more about `IInAppBillingVerifyPurchase` in the [Securing Purchases](SecuringPurchases.md) documentation.


Expand Down
4 changes: 2 additions & 2 deletions docs/SecuringPurchases.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ try
{
var productId = "mysku";

var connected = await CrossInAppBilling.Current.ConnectAsync(ItemType.InAppPurchase);
var connected = await CrossInAppBilling.Current.ConnectAsync();

if (!connected)
{
Expand All @@ -54,7 +54,7 @@ try

var verify = DependencyService.Get<IInAppBillingVerifyPurchase>();
//try to purchase item
var purchase = await CrossInAppBilling.Current.PurchaseAsync(productId, ItemType.InAppPurchase, "apppayload", verify);
var purchase = await CrossInAppBilling.Current.PurchaseAsync(productId, ItemType.InAppPurchase, verify);
if(purchase == null)
{
//Not purchased, may also throw excpetion to catch
Expand Down
2 changes: 1 addition & 1 deletion docs/TestingAndTroubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ You will not be able to test any StoreKit functionality until you have an iOS Pa
* Just open the app you're trying to test
* Your app will prompt you to sign in
* Enter your credentials for your sandbox test account
* While you could e.g. read your items, you need a real device to test purchasing. Purchasing does not work on Simulators.
* While you could e.g. read your items, you need a real device to test purchasing. NO apis work on Simulators.


## Android Testing
Expand Down
10 changes: 7 additions & 3 deletions src/Plugin.InAppBilling/InAppBilling.apple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,15 +404,19 @@ public void RequestFailed(SKRequest request, NSError error) =>

public void ReceivedResponse(SKProductsRequest request, SKProductsResponse response)
{
var product = response.Products;
var invalidProduct = response.InvalidProducts;
if (invalidProduct?.Any() ?? false)
{
tcsResponse.TrySetException(new InAppBillingPurchaseException(PurchaseError.InvalidProduct, $"Invalid Product: {invalidProduct.First()}"));
return;
}

var product = response.Products;
if (product != null)
{
tcsResponse.TrySetResult(product);
return;
}

tcsResponse.TrySetException(new InAppBillingPurchaseException(PurchaseError.InvalidProduct, "Invalid Product"));
}
}

Expand Down

0 comments on commit d4b1b04

Please sign in to comment.