Skip to content

Commit

Permalink
[MINI-1736] JS SDK API for In-App Purchase (#243)
Browse files Browse the repository at this point in the history
rleojoseph authored Feb 16, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 38bbb86 commit 8590d58
Showing 25 changed files with 806 additions and 15 deletions.
76 changes: 67 additions & 9 deletions js-miniapp-bridge/src/common-bridge.ts
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@ import {
import { ShareInfoType } from './types/share-info';
import { AccessTokenData, NativeTokenData } from './types/token-data';
import { MiniAppError, parseMiniAppError } from './types/error-types';
import { MiniAppResponseInfo } from './types/response-types/miniapp';
import { Product, PurchasedProduct } from './types/in-app-purchase';

/** @internal */
const mabMessageQueue: Callback[] = [];
@@ -80,15 +82,17 @@ export class MiniAppBridge {
this.executor = executor;
this.platform = executor.getPlatform();

window.addEventListener(
MiniAppSecureStorageEvents.onReady,
() => (this.isSecureStorageReady = true)
);
window.addEventListener(
MiniAppSecureStorageEvents.onLoadError,
(e: CustomEvent) =>
(this.secureStorageLoadError = parseMiniAppError(e.detail.message))
);
if (window) {
window.addEventListener(
MiniAppSecureStorageEvents.onReady,
() => (this.isSecureStorageReady = true)
);
window.addEventListener(
MiniAppSecureStorageEvents.onLoadError,
(e: CustomEvent) =>
(this.secureStorageLoadError = parseMiniAppError(e.detail.message))
);
}
}

/**
@@ -680,6 +684,60 @@ export class MiniAppBridge {
);
});
}

/**
* This will retrieve the list of products details available for In-App Purchases associated with Mini App in the Platform.
* @returns List of In-app purchase products
* @see {getAllProducts}
*/
getAllProducts() {
return new Promise<Product[]>((resolve, reject) => {
return this.executor.exec(
'getAllProducts',
null,
productsList => {
resolve(JSON.parse(productsList) as Product[]);
},
error => reject(parseMiniAppError(error))
);
});
}

/**
* This will request for the In-App Purchase of a product with product id associated with Mini App in the Platform.
* @param id Product id of the product to be purchased.
* @returns Purchased product details and the transaction details of the purchase.
*/
purchaseProductWith(id: string) {
return new Promise<PurchasedProduct>((resolve, reject) => {
return this.executor.exec(
'purchaseProductWith',
{ product_id: id },
purchasedProduct => {
resolve(JSON.parse(purchasedProduct) as PurchasedProduct);
},
error => reject(parseMiniAppError(error))
);
});
}

/**
* This will request to Consume the product that is purchased using purchaseProductWith API
* @param id Product id of the product that is purchased.
* @returns
*/
consumePurchaseWith(id: string, transactionId: string) {
return new Promise<MiniAppResponseInfo>((resolve, reject) => {
return this.executor.exec(
'purchaseProductWith',
{ product_id: id, transaction_id: transactionId },
consumedInfo => {
resolve(JSON.parse(consumedInfo) as MiniAppResponseInfo);
},
error => reject(parseMiniAppError(error))
);
});
}
}

/**
8 changes: 8 additions & 0 deletions js-miniapp-bridge/src/index.ts
Original file line number Diff line number Diff line change
@@ -44,6 +44,11 @@ import {
MiniAppSecureStorageSize,
MiniAppSecureStorageEvents,
} from './types/secure-storage';
import {
Product,
PurchasedProduct,
ProductPrice,
} from './types/in-app-purchase';

export {
MiniAppBridge,
@@ -80,4 +85,7 @@ export {
SecureStorageIOError,
MiniAppSecureStorageEvents,
CloseAlertInfo,
Product,
PurchasedProduct,
ProductPrice,
};
17 changes: 17 additions & 0 deletions js-miniapp-bridge/src/types/in-app-purchase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface Product {
title: string;
description: string;
id: string;
price: ProductPrice;
}

export interface ProductPrice {
currencyCode: string;
price: string;
}

export interface PurchasedProduct {
product: Product;
transactionId: string;
transactionDate: string;
}
7 changes: 7 additions & 0 deletions js-miniapp-bridge/src/types/response-types/miniapp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Generic Mini app Response object
*/
export interface MiniAppResponseInfo {
title?: string;
description?: string;
}
60 changes: 60 additions & 0 deletions js-miniapp-bridge/test/test.spec.ts
Original file line number Diff line number Diff line change
@@ -1007,6 +1007,66 @@ describe('console.log', () => {
});
});

describe('getAllProducts', () => {
it('will list the products', () => {
const bridge = new Bridge.MiniAppBridge(mockExecutor);
const response =
'[{"title": "MyApp_A","description": "This is app A for purchase","id": "com.rakuten.myappa","price": {"currencyCode": "yen","price": "100"}},{"title": "MyApp_B","description": "This is app B for purchase","id": "com.rakuten.myappb","price":{"currencyCode":"yen","price":"100"}}]';
mockExecutor.exec.callsArgWith(2, response);

const expected = [
{
title: 'MyApp_A',
description: 'This is app A for purchase',
id: 'com.rakuten.myappa',
price: {
currencyCode: 'yen',
price: '100',
},
},
{
title: 'MyApp_B',
description: 'This is app B for purchase',
id: 'com.rakuten.myappb',
price: {
currencyCode: 'yen',
price: '100',
},
},
];

return expect(bridge.getAllProducts()).to.eventually.deep.equal(expected);
});
});

describe('purchaseProductWith', () => {
it('will purchase product with id', () => {
const bridge = new Bridge.MiniAppBridge(mockExecutor);
const response =
'[{"product": {"title": "MyApp_A","description": "This is app A for purchase","id": "com.rakuten.myappa","price": {"currencyCode": "yen","price": "100"}},"transactionId": "transction_id_a","transactionDate": "2023/02/14"}]';
mockExecutor.exec.callsArgWith(2, response);
const expected = [
{
product: {
title: 'MyApp_A',
description: 'This is app A for purchase',
id: 'com.rakuten.myappa',
price: {
currencyCode: 'yen',
price: '100',
},
},
transactionId: 'transction_id_a',
transactionDate: '2023/02/14',
},
];
const productId = 'com.rakuten.myappa';
return expect(
bridge.purchaseProductWith(productId)
).to.eventually.deep.equal(expected);
});
});

interface CreateCallbackParams {
onSuccess?: (success: any) => any;
onError?: (error: string) => any;
8 changes: 7 additions & 1 deletion js-miniapp-sample/src/pages/auth-token.js
Original file line number Diff line number Diff line change
@@ -28,6 +28,12 @@ import { requestCustomPermissions } from '../services/permissions/actions';
import { requestAccessToken } from '../services/user/actions';

const useStyles = makeStyles((theme) => ({
card: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginTop: '40px',
},
wrapper: {
position: 'relative',
marginTop: 20,
@@ -225,7 +231,7 @@ function AuthToken(props: AuthTokenProps) {
}

return (
<GreyCard height="auto">
<GreyCard height="auto" className={classes.card}>
<CardContent>
<FormGroup column="true" classes={{ root: classes.rootFormGroup }}>
<Fragment>
4 changes: 2 additions & 2 deletions js-miniapp-sample/src/pages/home.js
Original file line number Diff line number Diff line change
@@ -9,8 +9,8 @@ import {
import clsx from 'clsx';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';

import ToolBar from '../components/ToolBar';
import { navItems } from './../routes';
import ToolBar from '../components/ToolBar';

const DRAWER_WIDTH = '250px';
const DRAWER_SHRINKED_WIDTH = '70px';
@@ -27,7 +27,7 @@ const useStyles = makeStyles((theme) => ({
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
justifyContent: 'initial',
alignItems: 'center',
},
drawerClosed: {
Loading

0 comments on commit 8590d58

Please sign in to comment.