A Node.js module for subscription and purchase processing.
yarn add paying
# or
npm install paying
- MongoDB
- Node.js
const paying = new Paying(
{
// every different payment method has its own configuration
// check the service's documentation for more information
alipay: new AlipayService(),
'apple-iap': new AppleIAPService(),
},
{repository: {url: 'mongodb://localhost:27017', database: 'paying-db'}},
);
"Managed service" means the data is managed by the paying service provider. like Apple IAP, Google Play, etc.
When you are using a managed service, all the data is synced from the service provider. so the operations like: renewal, cancellation, change subscription plan. are all managed by the service provider. and you can't do these operations programmatically.
-
Handle receipt
await paying.handleReceipt("apple-iap", userId, <received-receipt>); let user = paying.user(userId); // if receipt is valid, and contains a active subscription: console.log(user.subscriptions[0].isActive); // true
-
Handle callback
router('/apple/callback', req => { await paying.handleCallback('apple-iap', req); });
Opposite with managed service. The data and subscription is managed by yourself, like Alipay or something else. you can send a renewal or cancellation request to the paying service provider programmatically. and run several cron job to check transaction / subscription status periodically.
-
prepare a subscription
let {subscription, response} = await paying.prepareSubscription('alipay', { product: {id: 'monthly' as ProductId, group: 'membership'}, userId: 'xiaoming' as UserId, }); subscription.status === 'pending'; // true // return response to client to send an purchase
-
There are two ways to confirm a subscription status.
-
Check subscription and payment status
await paying.checkTransaction('alipay', subscription.latestTransaction.id); await subscription.refresh(); // if user paid: subscription.status === 'active'; // true // or maybe user canceled: subscription.status === 'canceled'; // true // or maybe still pending:
-
Handle callback to update payment status
router('/alipay/callback', req => { await paying.handleCallback('alipay', req.data); await subscription.refresh(); // if user paid: subscription.status === 'active'; // true // or maybe user canceled: subscription.status === 'canceled'; // true });
-
-
send a subscription renewal request to the paying service provider.
// if subscription needs to be renewal, and the subscription is still active: let transactionToBeRenewed = await paying.getSubscription(lastSubscriptionId); transactionToBeRenewed.expiresAt; // 2022-5-1 00:00:00 (expiresAt is a timestamp, here formatted to a date for ease of reading) await paying.checkSubscriptionRenewal('self-hosted', error => { // handle error here }); await subscription.refresh(); subscription.expiresAt; // 2022-6-1 00:00:00
-
cancel a subscription
subscription can be cancelled by two ways, either by our program or by the user cancelled at paying service provider.
-
Cancel subscription programmatically
await paying.cancelSubscription('alipay', subscription); // true;
-
subscription canceled by paying service provider
await paying.handleCallback('alipay', data);
-
interface AppleConfig {
sharedSecret: string;
}
export interface AppleProduct {
id: ProductId;
group?: string;
// duration milliseconds, only for subscription products
duration?: number;
}
new AppleService(config, products);
more details https://www.yuque.com/chenqiu/alipay-node-sdk/config-sdk
interface AlipayConfig {
/**
* callback urls which will be called by the paying service provider.
*/
signedCallbackURL: string;
paidCallbackURL: string;
gateway?: string;
appId: string;
privateKey: string;
appCert: string;
alipayPublicCert: string;
alipayRootCert: string;
}
interface AlipayPurchaseProduct {
id: ProductId;
group?: string;
subject: string;
amount: number;
}
interface AlipaySubscriptionProduct {
id: ProductId;
group?: string;
subject: string;
amount: number;
maxAmount?: number;
unit: 'MONTH' | 'DAY';
/**
* based on unit.
*
*
*/
duration: number;
}
new Alipay(config, products);
▸ user(id
): Promise
<User
>
query user by id from repository, including all subscriptions and transactions
Name | Type | Description |
---|---|---|
id |
__RefinedNominalType <string , Record <typeof __nominal , { user-id : true }>> |
user ID |
Promise
<User
>
▸ prepareSubscription(serviceName
, options
): Promise
<{ response
: unknown
; subscription
: Subscription
}>
prepare a new subscription. which will create a pending subscription, and wait to be confirmed by callback or scheduled checks
Name | Type | Description |
---|---|---|
serviceName |
TServiceKey |
Specify a service to process. refers to keys(this.services) |
options |
PrepareSubscriptionOptions |
Promise
<{ response
: unknown
; subscription
: Subscription
}>
} returns a promise that resolves to the subscription created just now and response usually send to client to purchase.
▸ preparePurchase(serviceName
, productId
, userId
): Promise
<any
>
Similar to prepareSubscription, but it will create a new pending purchase
Name | Type |
---|---|
serviceName |
TServiceKey |
product |
ProductId |
userId |
__RefinedNominalType <string , Record <typeof __nominal , { user-id : true }>> |
Promise
<any
>
▸ cancelSubscription(serviceName
, subscriptionOrId
): Promise
<boolean
>
Try to cancel a subscription. it will send an cancellation request to payment server, and the payment well be cancelled only if payment server returns success
Name | Type |
---|---|
serviceName |
TServiceKey |
subscriptionOrId |
Subscription | OriginalTransactionId |
Promise
<boolean
>
▸ checkSubscriptionRenewal(serviceName
, onError?
): Promise
<void
>
according to config.renewalBefore. send a renew request for subscriptions which expires date less than (now + config.renewalBefore)
Name | Type |
---|---|
serviceName |
TServiceKey |
onError? |
(error : unknown ) => void |
Promise
<void
>
▸ checkTransaction(serviceName
, id
): Promise
<void
>
Name | Type |
---|---|
serviceName |
TServiceKey |
id |
TransactionId |
Promise
<void
>
▸ checkTransactions(serviceName
, onError?
): Promise
<void
>
call this method periodically. to confirm pending transaction or cancel expired transaction.
Name | Type |
---|---|
serviceName |
TServiceKey |
onError? |
(error : unknown ) => void |
Promise
<void
>
▸ checkUncompletedSubscription(serviceName
, onError?
): Promise
<void
>
check all pending subscriptions. complete or cancel them if status has been settled.
Name | Type |
---|---|
serviceName |
TServiceKey |
onError? |
(error : unknown ) => void |
Promise
<void
>
▸ getSubscription(serviceName
, id
): Promise
<undefined
| Subscription
>
Query subscription by id. it also contains all transactions related to subscription
Name | Type |
---|---|
serviceName |
TServiceKey |
id |
OriginalTransactionId |
Promise
<undefined
| Subscription
>
▸ getTransaction(serviceName
, id
): Promise
<undefined
| AbstractTransaction
>
Query transaction by id
Name | Type | Description |
---|---|---|
serviceName |
TServiceKey |
|
id |
TransactionId |
transaction id |
Promise
<undefined
| AbstractTransaction
>
could be SubscriptionTransaction Or PurchaseTransaction
▸ handleCallback(serviceName
, data
): Promise
<void
>
Handle callback from payment service. which may contains actions like:
- payment-confirmed
- subscribed
- recharge-failed
- ... check {Actions} for more details
Name | Type |
---|---|
serviceName |
TServiceKey |
data |
unknown |
Promise
<void
>
void
▸ handleReceipt(serviceName
, userId
, receipt
): Promise
<void
>
Handle receipt from client. send receipt to validation server to validate and retrieve subscription/purchase status.
Name | Type |
---|---|
serviceName |
TServiceKey |
userId |
__RefinedNominalType <string , Record <typeof __nominal , { user-id : true }>> |
receipt |
unknown |
Promise
<void
>
MIT License.