Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safety async completions (unhook events, cancel task completion sources, etc) & Add CancellationTokens #604

Merged
merged 13 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
*.userprefs

# Build results
.idea/
src/.idea/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
## In-App Billing Plugin for .NET MAUI, Xamarin, and Windows
## In-App Billing Plugin for .NET MAUI and Windows

A simple In-App Purchase plugin for .NET MAUI, Xamarin, and Windows to query item information, purchase items, restore items, and more.
A simple In-App Purchase plugin for .NET MAUI and Windows to query item information, purchase items, restore items, and more.

Subscriptions are supported on iOS, Android, and Mac. Windows/UWP/WinUI 3 - does not support subscriptions at this time.

## Important Version Information
* v8 now supports .NET 8+ .NET MAUI and Windows Apps.
* v7 now supports .NET 6+, .NET MAUI, UWP, and Xamarin/Xamarin.Forms projects
* v7 is built against Android Billing Library 6.0
* See migration guides below
Expand All @@ -19,13 +20,11 @@ Get started by reading through the [In-App Billing Plugin documentation](https:/

|Platform|Version|
| ------------------- | :------------------: |
|Xamarin.iOS & iOS for .NET|10+|
|Xamarin.Mac, macOS for .NET, macCatlyst for .NET |All|
|Xamarin.TVOS, tvOS for .NET|10.13.2|
|Xamarin.Android, Android for .NET|21+|
|Windows 10 UWP|10+|
|iOS for .NET|10+|
|macCatlyst for .NET |All|
|tvOS for .NET|10.13.2|
|Android for .NET|21+|
|Windows App SDK (WinUI 3) |10+|
|Xamarin.Forms|All|
|.NET MAUI|All|

### Created By: [@JamesMontemagno](http://github.com/jamesmontemagno)
Expand Down
3 changes: 2 additions & 1 deletion docs/CheckAndRestorePurchases.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ When users get a new device or re-install your application it is best practice t
/// Get all current purchases for a specified product type.
/// </summary>
/// <param name="itemType">Type of product</param>
/// <param name="cancellationToken">Cancel the request</param>
/// <returns>The current purchases</returns>
Task<IEnumerable<InAppBillingPurchase>> GetPurchasesAsync(ItemType itemType);
Task<IEnumerable<InAppBillingPurchase>> GetPurchasesAsync(ItemType itemType, CancellationToken cancellationToken = default);
```

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 Down
6 changes: 4 additions & 2 deletions docs/PurchaseConsumable.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ Consumables are unique and work a bit different on each platform and the `Consum
/// <param name="itemType">Type of product being requested</param>
/// <param name="obfuscatedAccountId">Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.</param>
/// <param name="obfuscatedProfileId">Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.</param>
/// <param name="cancellationToken">Cancel the request.</param>
/// <returns>Purchase details</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occurs during processing</exception>
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null);
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null, CancellationToken cancellationToken = default);
```

#### obfuscatedAccountId & obfuscatedProfileId
Expand All @@ -44,9 +45,10 @@ Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, st
/// </summary>
/// <param name="productId">Id or Sku of product</param>
/// <param name="transactionIdentifier">Original Purchase Token</param>
/// <param name="cancellationToken">Cancel the request</param>
/// <returns>If consumed successful</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occurs during processing</exception>
Task<InAppBillingPurchase> ConsumePurchaseAsync(string productId, string transactionIdentifier);
Task<InAppBillingPurchase> ConsumePurchaseAsync(string productId, string transactionIdentifier, CancellationToken cancellationToken = default);
```


Expand Down
5 changes: 3 additions & 2 deletions docs/PurchaseNonConsumable.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ All purchases go through the `PurchaseAsync` method and you must always `Connect
/// <param name="itemType">Type of product being requested</param>
/// <param name="obfuscatedAccountId">Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.</param>
/// <param name="obfuscatedProfileId">Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.</param>
/// <param name="cancellationToken">Cancel the request</param>
/// <returns>Purchase details</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occurs during processing</exception>
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null);
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null, CancellationToken cancellationToken = default);
```

On Android you must call `FinalizePurchaseAsync` 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.
Expand Down Expand Up @@ -52,7 +53,7 @@ public async Task<bool> PurchaseItem(string productId)
else if(purchase.State == PurchaseState.Purchased)
{
// only need to finalize if on Android unless you turn off auto finalize on iOS
var ack = await CrossInAppBilling.Current.FinalizePurchaseAsync(purchase.TransactionIdentifier);
var ack = await CrossInAppBilling.Current.FinalizePurchaseAsync([purchase.TransactionIdentifier]);

// Handle if acknowledge was successful or not
}
Expand Down
5 changes: 3 additions & 2 deletions docs/PurchaseSubscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ All purchases go through the `PurchaseAsync` method and you must always `Connect
/// <param name="itemType">Type of product being requested</param>
/// <param name="obfuscatedAccountId">Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.</param>
/// <param name="obfuscatedProfileId">Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.</param>
/// <param name="cancellationToken">Cancel the request.</param>
/// <returns>Purchase details</returns>
/// <exception cref="InAppBillingPurchaseException">If an error occurs during processing</exception>
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null);
Task<InAppBillingPurchase> PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null, CancellationToken cancellationToken = default);
```

On Android you must call `FinalizePurchaseAsync` 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.
Expand Down Expand Up @@ -52,7 +53,7 @@ public async Task<bool> PurchaseItem(string productId, string payload)
else if(purchase.State == PurchaseState.Purchased)
{
//only needed on android unless you turn off auto finalize
var ack = await CrossInAppBilling.Current.FinalizePurchaseAsync(purchase.TransactionIdentifier);
var ack = await CrossInAppBilling.Current.FinalizePurchaseAsync([purchase.TransactionIdentifier]);

// Handle if acknowledge was successful or not
}
Expand Down
2 changes: 1 addition & 1 deletion docs/SecuringPurchases.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Each platform handles security of In-App Purchases a bit different. To handle this whenever you make a purchase you should use the date from the purchase to validate on your backend.

## Recommended Reading:
* [Xamarin.iOS Securing Purchases Documentation](https://developer.xamarin.com/guides/ios/platform_features/in-app_purchasing/transactions_and_verification/#Securing_Purchases)
* [iOS Securing Purchases Documentation](https://developer.xamarin.com/guides/ios/platform_features/in-app_purchasing/transactions_and_verification/#Securing_Purchases)
* [Google Play service Security and Design](https://developer.android.com/google/play/billing/billing_best_practices.html)


Expand Down
Loading
Loading