cordova-plugin-purchase
Advanced tools
Comparing version 13.0.3 to 13.1.1
@@ -224,10 +224,10 @@ # Class: Adapter | ||
Handle platform specific fields from receipt validation response. | ||
Handle a response from a receipt validation process. | ||
#### Parameters | ||
| Name | Type | | ||
| :------ | :------ | | ||
| `receipt` | [`Receipt`](CdvPurchase.Receipt.md) | | ||
| `response` | [`Payload`](../modules/CdvPurchase.Validator.Response.md#payload) | | ||
| Name | Type | Description | | ||
| :------ | :------ | :------ | | ||
| `receipt` | [`Receipt`](CdvPurchase.Receipt.md) | The receipt being validated. | | ||
| `response` | [`Payload`](../modules/CdvPurchase.Validator.Response.md#payload) | The response payload from the receipt validation process. | | ||
@@ -238,2 +238,4 @@ #### Returns | ||
A promise that resolves when the response has been handled. | ||
#### Implementation of | ||
@@ -240,0 +242,0 @@ |
@@ -334,10 +334,28 @@ # Class: Adapter | ||
Request a payment from the user | ||
This function simulates a payment process by prompting the user to confirm the payment. | ||
It creates a `Receipt` and `Transaction` object and returns the `Transaction` object if the user enters "Y" in the prompt. | ||
**`Example`** | ||
const paymentRequest = { | ||
amountMicros: 1000000, | ||
currency: "USD", | ||
items: [{ id: "product-1" }, { id: "product-2" }] | ||
}; | ||
const result = await requestPayment(paymentRequest); | ||
if (result?.isError) { | ||
console.error(`Error: ${result.message}`); | ||
} else if (result) { | ||
console.log(`Transaction approved: ${result.transactionId}`); | ||
} else { | ||
console.log("Payment cancelled by user"); | ||
} | ||
#### Parameters | ||
| Name | Type | | ||
| :------ | :------ | | ||
| `paymentRequest` | [`PaymentRequest`](../interfaces/CdvPurchase.PaymentRequest.md) | | ||
| `additionalData?` | [`AdditionalData`](../interfaces/CdvPurchase.AdditionalData.md) | | ||
| Name | Type | Description | | ||
| :------ | :------ | :------ | | ||
| `paymentRequest` | [`PaymentRequest`](../interfaces/CdvPurchase.PaymentRequest.md) | An object containing information about the payment, such as the amount and currency. | | ||
| `additionalData?` | [`AdditionalData`](../interfaces/CdvPurchase.AdditionalData.md) | Additional data to be included in the receipt. | | ||
@@ -348,2 +366,5 @@ #### Returns | ||
A promise that resolves to either an error object (if the user enters "E" in the prompt), | ||
a `Transaction` object (if the user confirms the payment), or `undefined` (if the user does not confirm the payment). | ||
#### Implementation of | ||
@@ -350,0 +371,0 @@ |
@@ -15,10 +15,28 @@ # Interface: PaymentRequest | ||
platform: Platform.BRAINTREE, | ||
productIds: ['my-product-1', 'my-product-2'], | ||
amountMicros: 1990000, | ||
items: [{ | ||
id: 'margherita_large', | ||
title: 'Pizza Margherita Large', | ||
pricing: { | ||
priceMicros: 9990000, | ||
} | ||
}, { | ||
id: 'delivery_standard', | ||
title: 'Delivery', | ||
pricing: { | ||
priceMicros: 2000000, | ||
} | ||
}] | ||
amountMicros: 11990000, | ||
currency: 'USD', | ||
description: 'This this the description of the payment request', | ||
}).then((result) => { | ||
if (result && result.isError && result.code !== ErrorCode.PAYMENT_CANCELLED) { | ||
alert(result.message); | ||
} | ||
}) | ||
.cancelled(() => { // user cancelled by closing the window | ||
}) | ||
.failed(error => { // payment request failed | ||
}) | ||
.initiated(transaction => { // transaction initiated | ||
}) | ||
.approved(transaction => { // transaction approved | ||
}) | ||
.finished(transaction => { // transaction finished | ||
}); | ||
@@ -36,5 +54,5 @@ ``` | ||
- [email](CdvPurchase.PaymentRequest.md#email) | ||
- [items](CdvPurchase.PaymentRequest.md#items) | ||
- [mobilePhoneNumber](CdvPurchase.PaymentRequest.md#mobilephonenumber) | ||
- [platform](CdvPurchase.PaymentRequest.md#platform) | ||
- [productIds](CdvPurchase.PaymentRequest.md#productids) | ||
@@ -45,6 +63,8 @@ ## Properties | ||
• **amountMicros**: `number` | ||
• `Optional` **amountMicros**: `number` | ||
Amount to pay. Required. | ||
Amount to pay. | ||
Default to the sum of all items. | ||
___ | ||
@@ -68,2 +88,4 @@ | ||
Default to the currency of the items. | ||
___ | ||
@@ -87,2 +109,12 @@ | ||
### items | ||
• **items**: (`undefined` \| [`PaymentRequestItem`](CdvPurchase.PaymentRequestItem.md))[] | ||
Products being purchased. | ||
They do not have to be products registered with the plugin, but they can be. | ||
___ | ||
### mobilePhoneNumber | ||
@@ -103,11 +135,1 @@ | ||
Platform that will handle the payment request. | ||
___ | ||
### productIds | ||
• **productIds**: `string`[] | ||
Products being purchased. | ||
Used for your reference, does not have to be a product registered with the plugin. |
@@ -64,2 +64,3 @@ # Namespace: CdvPurchase | ||
- [PaymentRequest](../interfaces/CdvPurchase.PaymentRequest.md) | ||
- [PaymentRequestItem](../interfaces/CdvPurchase.PaymentRequestItem.md) | ||
- [PostalAddress](../interfaces/CdvPurchase.PostalAddress.md) | ||
@@ -154,3 +155,3 @@ - [PricingPhase](../interfaces/CdvPurchase.PricingPhase.md) | ||
• `Const` **PLUGIN\_VERSION**: ``"13.0.3"`` | ||
• `Const` **PLUGIN\_VERSION**: ``"13.1.1"`` | ||
@@ -157,0 +158,0 @@ Current release number of the plugin. |
{ | ||
"name": "cordova-plugin-purchase", | ||
"version": "13.0.3", | ||
"version": "13.1.1", | ||
"description": "Cordova Purchase plugin for iOS, Android, Windows (AppStore, Play, UWP)", | ||
@@ -5,0 +5,0 @@ "cordova": { |
@@ -84,2 +84,4 @@ # Cordova Purchase Plugin | ||
### Learning about In-App Purchases | ||
If you wish to learn more about In-App Purchases (IAP), you'll find a good overview on the subject from the various platforms documentation: | ||
@@ -98,10 +100,15 @@ | ||
To ease the beginning of your journey into the intimidating world of In-App Purchase with Cordova, we wrote a guide which hopefully will help you get things done: [Guide: Cordova In-App Purchase Plugin - v13.0](https://purchase.cordova.fovea.cc/v/v13.0/) | ||
### Using the Plugin | ||
In short, you'll have two main tasks to accomplish: | ||
To ease the beginning of your journey into the intimidating world of In-App Purchase with Cordova, we wrote a guide which hopefully will help you get things done: | ||
1. Setup your application and In-App Products on AppStore, Play or Azure platforms using their respective web interfaces. | ||
* [Guide: Cordova In-App Purchase Plugin - v13.0](https://purchase.cordova.fovea.cc/v/v13.0/) | ||
You'll have two main tasks to accomplish: | ||
1. Setup your application and In-App Products on AppStore, Play, Braintree or Azure platforms using their respective web interfaces. | ||
2. Add In-App Purchase code to your application. | ||
For platform setup, the [wiki](https://github.com/j3k0/cordova-plugin-purchase/wiki/Home) is a good starting point. | ||
There's a specific page for the [version 13] release(./wiki/Version-13). | ||
@@ -108,0 +115,0 @@ |
# Release Notes - Cordova Plugin Purchase | ||
## 13.0.0 | ||
## 13.1 | ||
- [Migration guide](https://github.com/j3k0/cordova-plugin-purchase/wiki/HOWTO:-Migrate-to-v13) | ||
### Update to requestPayment() | ||
In the payment request, the `items` array now replace the `productIds` array. Use this array to define the list of items the user is paying for. For example: | ||
```ts | ||
CdvPurchase.store.requestPayment({ | ||
platform: CdvPurchase.Platform.BRAINTREE, | ||
amountMicros: 11000000, | ||
currency: 'USD', | ||
items: [{ | ||
id: 'margherita_large', | ||
title: 'Pizza Margherita Large', | ||
pricing: { | ||
priceMicros: 10000000, | ||
} | ||
}, { | ||
id: 'delivery_standard', | ||
title: 'Delivery', | ||
pricing: { | ||
priceMicros: 1000000, | ||
} | ||
}] | ||
}); | ||
``` | ||
The format for items makes them compatible with products loaded from the stores. You can then manage your inventory on Google Play but allow payment for those Google Play products using Braintree: | ||
```ts | ||
store.register([{ | ||
id: 'five_tokens', | ||
type: ProductType.CONSUMABLE | ||
platform: Platform.GOOGLE_PLAY, | ||
}]); | ||
// Later on... | ||
store.requestPayment({ | ||
platform: CdvPurchase.Platform.BRAINTREE, | ||
amountMicros: 11000000, | ||
currency: 'USD', | ||
items: [store.get('five_tokens')], | ||
}); | ||
``` | ||
See [PaymentRequest](https://github.com/j3k0/cordova-plugin-purchase/blob/master/api/interfaces/CdvPurchase.PaymentRequest.md) and [PaymentRequestItem](https://github.com/j3k0/cordova-plugin-purchase/blob/master/api/interfaces/CdvPurchase.PaymentRequestItem.md) for details. | ||
### Fixes for Braintree.requestPayment() | ||
Bug fixes: | ||
* Properly using the provided `applePayOptions` | ||
* Detecting payment request cancellations by user | ||
### requestPayment() amount computed from items | ||
If the amount is not provided in a payment request, it will be computed as the sum of all items. | ||
Currency will also be retrieved from the items when missing. | ||
## 13.0 | ||
This is a full rewrite of the API, updated to allow: | ||
* using multiple payment processors in parallel | ||
* exposing multiple offers for a single product and complex pricing | ||
* exposing purchases from receipts (either local receipts or verified from a server) | ||
* placing custom payment requests | ||
All JavaScript code has being rewritten in TypeScript, typings are now 100% complete and accurate. | ||
If you're upgrading from an earlier version, check the [migration guide](https://github.com/j3k0/cordova-plugin-purchase/wiki/HOWTO:-Migrate-to-v13). | ||
The native code is built using version 12 as starting point, so all features from version 12 are available as well. | ||
### Braintree | ||
This version introduces support for Braintree as a payment processor, it requires an additional plugin to add the libraries to your project: https://github.com/j3k0/cordova-plugin-purchase-braintree | ||
The Braintree integration supports payment with 3DSecure and Apple Pay. | ||
### Windows Store | ||
Support for payments on Windows Store has been dropped. It will be back in a later version. | ||
### Overview | ||
The new API separates the different concepts with their own first-level entities: | ||
- Products | ||
- Offers | ||
- Receipts | ||
- Transactions | ||
**Products / Offers** will contain the definition of what's available to the user to purchase. | ||
**Receipts / Transactions** will contain details about what the user has purchased. | ||
In the new API, it is possible to initiate transactions not necessarily linked with a product (using payment processors like Braintree). | ||
It defines a generic Adapter interface, implemented by the various payment platforms. The core of the plugin controls and monitors the different active adapters and expose the unified API. Previously, we basically had an iOS implementation of the unified API (using StoreKit), an android implementation, etc... Now, many adapters can coexist in peace. | ||
## 12.0.0 | ||
This was a first attempt to port the code to billing library v5. It's not recommended to use this version as, trying to keep the API backward compatible, made it messy and bug prone. Use version 13. | ||
### Upgrade to Google Play Billing library v5.0 | ||
@@ -10,0 +110,0 @@ |
@@ -36,3 +36,3 @@ // | ||
.approved(transaction => transaction.verify()) | ||
.verified(receipt => receipt.finish()) | ||
.verified(receipt => receipt.collection.find(t => t.id) receipt.finish()) | ||
.finished(transaction => console.log('Products owned: ' + transaction.products.map(p => p.id).join(','))) | ||
@@ -53,3 +53,22 @@ .receiptUpdated(r => updatePurchases(r)) | ||
store.initialize([Platform.APPLE_APPSTORE, Platform.GOOGLE_PLAY]) | ||
store.initialize([Platform.APPLE_APPSTORE, Platform.GOOGLE_PLAY, { | ||
platform: Platform.BRAINTREE, | ||
options: { | ||
applePay: { | ||
companyName: 'Total Car Check, LTD', | ||
preparePaymentRequest() { | ||
return { | ||
merchantCapabilities: [ApplePay.MerchantCapability.ThreeDS], | ||
paymentSummaryItems: [{ | ||
amount: '11.99', | ||
label: '1x Gold Check' | ||
}, { | ||
amount: '11.99', | ||
label: 'Total Car Check, LTD' | ||
}] | ||
} as CdvPurchase.ApplePay.PaymentRequest | ||
}, | ||
} | ||
} | ||
}]) | ||
.then(() => { | ||
@@ -124,3 +143,3 @@ console.log('Store Ready!'); | ||
amountMicros: 9990000, | ||
productIds: [], | ||
items: [store.get('five_tokens')], | ||
}) | ||
@@ -127,0 +146,0 @@ request |
@@ -82,2 +82,28 @@ namespace CdvPurchase { | ||
/** | ||
* Item being purchased with `requestPayment` | ||
* | ||
* The format is such as it's compatible with `Product`. This way, normal products can be added to | ||
* the payment request. | ||
*/ | ||
export interface PaymentRequestItem { | ||
/** Identifier */ | ||
id: string; | ||
/** Label for the item */ | ||
title: string; | ||
/** Item pricing information. | ||
* | ||
* It can be undefined if a single product is purchased. If that case, it's assumed the price | ||
* is equal to the total amount requested. */ | ||
pricing?: { | ||
/** Price in micro units (i.e. price * 1,000,000) */ | ||
priceMicros: number; | ||
/** Currency, for verification, if set it should be equal to the PaymentRequest currency */ | ||
currency?: string; | ||
} | ||
} | ||
/** | ||
* Request for payment. | ||
@@ -91,10 +117,28 @@ * | ||
* platform: Platform.BRAINTREE, | ||
* productIds: ['my-product-1', 'my-product-2'], | ||
* amountMicros: 1990000, | ||
* items: [{ | ||
* id: 'margherita_large', | ||
* title: 'Pizza Margherita Large', | ||
* pricing: { | ||
* priceMicros: 9990000, | ||
* } | ||
* }, { | ||
* id: 'delivery_standard', | ||
* title: 'Delivery', | ||
* pricing: { | ||
* priceMicros: 2000000, | ||
* } | ||
* }] | ||
* amountMicros: 11990000, | ||
* currency: 'USD', | ||
* description: 'This this the description of the payment request', | ||
* }).then((result) => { | ||
* if (result && result.isError && result.code !== ErrorCode.PAYMENT_CANCELLED) { | ||
* alert(result.message); | ||
* } | ||
* }) | ||
* .cancelled(() => { // user cancelled by closing the window | ||
* }) | ||
* .failed(error => { // payment request failed | ||
* }) | ||
* .initiated(transaction => { // transaction initiated | ||
* }) | ||
* .approved(transaction => { // transaction approved | ||
* }) | ||
* .finished(transaction => { // transaction finished | ||
* }); | ||
@@ -107,5 +151,5 @@ */ | ||
* | ||
* Used for your reference, does not have to be a product registered with the plugin. | ||
* They do not have to be products registered with the plugin, but they can be. | ||
*/ | ||
productIds: string[]; | ||
items: (PaymentRequestItem | undefined)[]; | ||
@@ -118,5 +162,7 @@ /** | ||
/** | ||
* Amount to pay. Required. | ||
* Amount to pay. | ||
* | ||
* Default to the sum of all items. | ||
*/ | ||
amountMicros: number; | ||
amountMicros?: number; | ||
@@ -127,2 +173,4 @@ /** | ||
* Some payment platforms only support one currency thus do not require this field. | ||
* | ||
* Default to the currency of the items. | ||
*/ | ||
@@ -129,0 +177,0 @@ currency?: string; |
@@ -155,3 +155,3 @@ namespace CdvPurchase { | ||
transaction.purchaseDate = new Date(); | ||
transaction.products = paymentRequest.productIds.map(productId => ({ id: productId })); | ||
transaction.products = paymentRequest.items?.filter(p => p).map(product => ({ id: product?.id || ''})) || []; | ||
transaction.state = TransactionState.APPROVED; | ||
@@ -170,3 +170,3 @@ transaction.transactionId = dropInResult.paymentMethodNonce?.nonce ?? `UNKNOWN_${dropInResult.paymentMethodType}_${dropInResult.paymentDescription}`; | ||
const transaction = new Transaction(Platform.BRAINTREE, this, decorator); | ||
transaction.products = paymentRequest.productIds.map(productId => ({ id: productId })); | ||
transaction.products = paymentRequest.items.filter(p => p).map(product => ({ id: product?.id || ''})); | ||
transaction.state = TransactionState.APPROVED; | ||
@@ -367,3 +367,3 @@ transaction.transactionId = dropInResult.paymentMethodNonce?.nonce ?? `UNKNOWN_${dropInResult.paymentMethodType}_${dropInResult.paymentDescription}`; | ||
return { | ||
id: receipt.paymentRequest.productIds?.[0] ?? 'unknown', | ||
id: receipt.paymentRequest.items?.[0]?.id ?? 'unknown', | ||
type: ProductType.CONSUMABLE, | ||
@@ -384,2 +384,9 @@ priceMicros: receipt.paymentRequest.amountMicros, | ||
/** | ||
* Handle a response from a receipt validation process. | ||
* | ||
* @param receipt The receipt being validated. | ||
* @param response The response payload from the receipt validation process. | ||
* @returns A promise that resolves when the response has been handled. | ||
*/ | ||
async handleReceiptValidationResponse(receipt: Receipt, response: Validator.Response.Payload): Promise<void> { | ||
@@ -419,3 +426,9 @@ this.log.info("receipt validation response: " + JSON.stringify(response)); | ||
const dropInResponseError = (log: Logger, response?: IError): IError => { | ||
/** | ||
* Returns the error response from Drop In | ||
* | ||
* If the "error" is that the user cancelled the payment, then returns undefined | ||
* (as per the specification for requestPayment) | ||
*/ | ||
const dropInResponseError = (log: Logger, response?: IError): (IError | undefined) => { | ||
if (!response) { | ||
@@ -427,2 +440,6 @@ log.warn("launchDropIn failed: no response"); | ||
// Failed | ||
if (response.code === ErrorCode.PAYMENT_CANCELLED) { | ||
log.info("User cancelled the payment request"); | ||
return undefined; | ||
} | ||
log.warn("launchDropIn failed: " + JSON.stringify(response)); | ||
@@ -429,0 +446,0 @@ return response; |
@@ -83,2 +83,3 @@ namespace CdvPurchase { | ||
this.clientTokenProvider = clientTokenProvider; | ||
this.applePayOptions = applePayOptions; | ||
} | ||
@@ -97,7 +98,14 @@ | ||
if (!request.paymentSummaryItems) { | ||
request.paymentSummaryItems = [{ | ||
const items = | ||
paymentRequest.items.filter(p => p).map((product, index) => ({ | ||
type: 'final', | ||
label: product?.title || product?.id || `Item #${index + 1}`, | ||
amount: `${Math.round((product?.pricing?.priceMicros ?? paymentRequest.amountMicros ?? 0) / 10000) / 100}`, | ||
} as ApplePay.PaymentSummaryItem)) | ||
const total: ApplePay.PaymentSummaryItem = { | ||
type: 'final', | ||
label: this.applePayOptions?.companyName ?? 'Total', | ||
type: 'final', | ||
amount: `${Math.round(paymentRequest.amountMicros / 10000) / 100}`, | ||
}]; | ||
amount: `${Math.round((paymentRequest.amountMicros ?? 0) / 10000) / 100}`, | ||
} | ||
request.paymentSummaryItems = [...items, total]; | ||
} | ||
@@ -104,0 +112,0 @@ const result = await ApplePayPlugin.requestPayment(request); |
@@ -140,6 +140,33 @@ namespace CdvPurchase { | ||
/** | ||
* This function simulates a payment process by prompting the user to confirm the payment. | ||
* | ||
* It creates a `Receipt` and `Transaction` object and returns the `Transaction` object if the user enters "Y" in the prompt. | ||
* | ||
* @param paymentRequest - An object containing information about the payment, such as the amount and currency. | ||
* @param additionalData - Additional data to be included in the receipt. | ||
* | ||
* @returns A promise that resolves to either an error object (if the user enters "E" in the prompt), | ||
* a `Transaction` object (if the user confirms the payment), or `undefined` (if the user does not confirm the payment). | ||
* | ||
* @example | ||
* | ||
* const paymentRequest = { | ||
* amountMicros: 1000000, | ||
* currency: "USD", | ||
* items: [{ id: "product-1" }, { id: "product-2" }] | ||
* }; | ||
* const result = await requestPayment(paymentRequest); | ||
* if (result?.isError) { | ||
* console.error(`Error: ${result.message}`); | ||
* } else if (result) { | ||
* console.log(`Transaction approved: ${result.transactionId}`); | ||
* } else { | ||
* console.log("Payment cancelled by user"); | ||
* } | ||
*/ | ||
async requestPayment(paymentRequest: PaymentRequest, additionalData?: CdvPurchase.AdditionalData): Promise<IError | Transaction | undefined> { | ||
await Utils.asyncDelay(100); // maybe app has some UI to update... and "prompt" prevents that | ||
const response = prompt(`Mock payment of ${paymentRequest.amountMicros / 1000000} ${paymentRequest.currency}. Enter "Y" to confirm. Enter "E" to trigger an error.`); | ||
const response = prompt(`Mock payment of ${(paymentRequest.amountMicros ?? 0) / 1000000} ${paymentRequest.currency}. Enter "Y" to confirm. Enter "E" to trigger an error.`); | ||
if (response?.toUpperCase() === 'E') return storeError(ErrorCode.PAYMENT_NOT_ALLOWED, 'Payment not allowed'); | ||
@@ -150,3 +177,3 @@ if (response?.toUpperCase() !== 'Y') return; | ||
transaction.purchaseDate = new Date(); | ||
transaction.products = paymentRequest.productIds.map(productId => ({ id: productId })); | ||
transaction.products = paymentRequest.items.filter(p => p).map(product => ({ id: product?.id || '' })), | ||
transaction.state = TransactionState.APPROVED; | ||
@@ -153,0 +180,0 @@ transaction.transactionId = 'payment-' + new Date().getTime(); |
@@ -27,3 +27,3 @@ /// <reference path="validator/validator.ts" /> | ||
*/ | ||
export const PLUGIN_VERSION = '13.0.3'; | ||
export const PLUGIN_VERSION = '13.1.1'; | ||
@@ -423,2 +423,28 @@ /** | ||
return PaymentRequestPromise.failed(ErrorCode.PAYMENT_NOT_ALLOWED, 'Adapter not found or not ready (' + paymentRequest.platform + ')'); | ||
// fill-in missing total amount as the sum of all items. | ||
if (typeof paymentRequest.amountMicros === 'undefined') { | ||
paymentRequest.amountMicros = 0; | ||
for (const item of paymentRequest.items) { | ||
paymentRequest.amountMicros += item?.pricing?.priceMicros ?? 0; | ||
} | ||
} | ||
// fill-in the missing if set in the items. | ||
if (typeof paymentRequest.currency === 'undefined') { | ||
for (const item of paymentRequest.items) { | ||
if (item?.pricing?.currency) { | ||
paymentRequest.currency = item.pricing.currency; | ||
} | ||
} | ||
} | ||
// fill-in item amount when there's just 1 item. | ||
if (paymentRequest.items.length === 1) { | ||
const item = paymentRequest.items[0]; | ||
if (item && !item.pricing) { | ||
item.pricing = { priceMicros: paymentRequest.amountMicros ?? 0 } | ||
} | ||
} | ||
const promise = new PaymentRequestPromise(); | ||
@@ -425,0 +451,0 @@ adapter.requestPayment(paymentRequest, additionalData).then(result => { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1828112
289
23504
176