Skip to content
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
18 changes: 18 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
TokenResponse,
type PaymentResult,
UnexpectedError,
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
PaymentSplit,
Comment thread
AbdulazizAlrabiah marked this conversation as resolved.
} from 'react-native-moyasar-sdk';

const paymentConfig = new PaymentConfig({
// givenId: '013d92f2-c67b-49c6-ae03-d7c548c771a2',
publishableApiKey: 'pk_test_U38gMHTgVv4wYCd35Zk1JSEd1ZyMYyA9oQ7T4rKa',
// baseUrl: 'https://apimig.moyasar.com', // Staging base URL
amount: 20001,
currency: 'SAR',
merchantCountryCode: 'SA',
Expand All @@ -42,6 +46,20 @@
manual: false,
}),
applyCoupon: true,
// splits: [ publishableApiKey for testing: 'pk_test_uQra5pwtUo9GaenMSS4XgfAmeLhmjUTJwFdXJxsH', baseUrl: 'https://apimig.moyasar.com'
// new PaymentSplit({
// recipientId: '7d2d0797-a2be-40fe-bb1b-1fdec9824c95',
// amount: 10001,
// }),
// new PaymentSplit({
// recipientId: '327680bb-d790-4643-8e10-31455a1ab3a6',
// amount: 10000,
// reference: 'optional-reference-for-split-1fcfcbe9-ba75-4eed',
// description: 'Platform processing fee',
// feeSource: true,
// refundable: true,
// }),
// ],
});

function onPaymentResult(paymentResult: PaymentResult) {
Expand Down Expand Up @@ -93,7 +111,7 @@
<SamsungPay
paymentConfig={paymentConfig}
onPaymentResult={onPaymentResult}
style={{ width: '90%' }}

Check warning on line 114 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Inline style: { width: '90%' }
/>
<CreditCard
paymentConfig={paymentConfig}
Expand All @@ -103,7 +121,7 @@
<ApplePay
paymentConfig={paymentConfig}
onPaymentResult={onPaymentResult}
style={{ buttonType: 'buy' }}

Check warning on line 124 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / lint

Inline style: { buttonType: 'buy' }
/>
</View>
</ScrollView>
Expand Down
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export * from './models/api/api_responses/token_response';
export * from './models/api/api_requests/token_request';
export * from './models/api/api_requests/payment_request';
export * from './models/component_models/moyasar_style';
export * from './models/payment_split';
19 changes: 19 additions & 0 deletions src/models/api/api_requests/payment_request.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,71 @@
import type { PaymentSplit } from '../../payment_split';
import type { PaymentRequestSource } from '../sources/payment_request_source';

/**
* Constructs a PaymentRequest object for creating a payment.
* @param {givenId | null} [givenId] - Optional UUID for the payment (UUID v4 is recommended). It will be attached with the payment creation request to support idempotency. `It is going be the ID of the created payment`.
* @param {string} [baseUrl='https://api.moyasar.com'] - The base URL for Moyasar API. Defaults to 'https://api.moyasar.com'.
* @param {number} amount - The amount to be charged in the smallest currency unit. For example, to charge `SAR 257.58` you will have the [amount] as `25758`. In other words, 10 SAR = 10 * 100 Halalas. Integer values only.
* @param {string} [currency='SAR'] - The currency code for the payment. Defaults to 'SAR'. Must be in ISO 4217 3-letter currency code format.
* @param {string | null} [description] - Can be any string you want to tag the payment. For example `Payment for Order #34321`.
* @param {Record<string, string | number | boolean> | null} [metadata] - Adds searchable key/value pairs to the payment. For example `{"size": "xl"}`.
* @param {PaymentRequestSource} source - A payment source object to be charged, such as Apple Pay source or Credit Card source.
* @param {string | null} [callbackUrl] - The URL to be redirected to after a 3D secure transaction (e.g., https://sdk.moyasar.com/return). Required for Credit Card payments.
* @param {boolean} [applyCoupon=true] - A flag to control the coupon application (based on the BIN). This key is required only if you don't want to apply the coupon. Otherwise, the coupon is going to be applied. Defaults to true.
* @param {PaymentSplit[] | null} [splits] - Optional array of `PaymentSplit` object used to distribute the charged amount (in the smallest currency unit) among multiple recipients or to collect a platform fee.
* - Each split requires `recipientId` and `amount` parameters.
* - `reference` and `description` parameters are optional.
* - Set `feeSource = true` parameter to mark the split as a fee/commission taken by the platform.
* - Set `refundable` parameter to control whether a split amount is refundable (`true`/`false`). Leave it to use the backend's default.
* - Set the `publishableApiKey` to "pk_test_uQra5pwtUo9GaenMSS4XgfAmeLhmjUTJwFdXJxsH" and set the `baseUrl` parameter to "https://apimig.moyasar.com" for staging testing.
Comment thread
AbdulazizAlrabiah marked this conversation as resolved.
*/
export class PaymentRequest {
givenId?: string | null;
baseUrl: string;
amount: number;
currency: string;
description?: string | null;
metadata?: Record<string, string | number | boolean> | null;
source: PaymentRequestSource;
callbackUrl?: string | null;
applyCoupon?: boolean;
splits?: PaymentSplit[] | null;

constructor({
givenId,
baseUrl = 'https://api.moyasar.com',
amount,
currency = 'SAR',
description,
metadata,
source,
callbackUrl,
applyCoupon = true,
splits,
}: {
givenId?: string | null;
baseUrl?: string;
amount: number;
currency?: string;
description?: string | null;
metadata?: Record<string, string | number | boolean> | null;
source: PaymentRequestSource;
callbackUrl?: string | null;
applyCoupon?: boolean;
splits?: PaymentSplit[] | null;
}) {
this.givenId = givenId;

const cleanedBaseUrl = baseUrl.replace(/\/+$/, ''); // Removes trailing slashes
this.baseUrl = cleanedBaseUrl;
this.amount = amount;
this.currency = currency;
this.description = description;
this.metadata = metadata;
this.source = source;
this.callbackUrl = callbackUrl;
this.applyCoupon = applyCoupon;
this.splits = splits;
}

toJson(): Record<string, any> {
Expand All @@ -60,6 +78,7 @@ export class PaymentRequest {
source: this.source.toJson(),
callback_url: this.callbackUrl,
apply_coupon: this.applyCoupon ?? true,
splits: this.splits?.map((split) => split.toJson()),
};
}
}
7 changes: 7 additions & 0 deletions src/models/api/api_requests/token_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @param {string} cvc - Credit Card's security code.
* @param {string} month - Two digit number representing the Credit Card's expiration month.
* @param {string} year - Two or four digit number representing the Credit Card's expiration year.
* @param {string} [baseUrl='https://api.moyasar.com'] baseUrl - The base URL for Moyasar API. Defaults to 'https://api.moyasar.com'.
* @param {string} callbackUrl - The URL to be redirected to after a 3D secure transaction (e.g., https://sdk.moyasar.com/return).
* @param {Record<string, string | number | boolean> | null} [metadata] - Adds searchable key/value pairs to the payment. For example `{"size": "xl"}`.
*/
Expand All @@ -14,6 +15,7 @@ export class TokenRequest {
cvc: string;
month: string;
year: string;
baseUrl: string;
callbackUrl: string;
metadata?: Record<string, string | number | boolean> | null;

Expand All @@ -23,6 +25,7 @@ export class TokenRequest {
cvc,
month,
year,
baseUrl = 'https://api.moyasar.com',
callbackUrl,
metadata,
}: {
Expand All @@ -31,6 +34,7 @@ export class TokenRequest {
cvc: string;
month: string;
year: string;
baseUrl?: string;
callbackUrl: string;
metadata?: Record<string, string | number | boolean> | null;
}) {
Expand All @@ -39,6 +43,9 @@ export class TokenRequest {
this.cvc = cvc;
this.month = month;
this.year = year;

const cleanedBaseUrl = baseUrl.replace(/\/+$/, ''); // Removes trailing slashes
this.baseUrl = cleanedBaseUrl;
this.callbackUrl = callbackUrl;
this.metadata = metadata;
}
Expand Down
10 changes: 10 additions & 0 deletions src/models/api/api_responses/payment_response.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PaymentSplit } from '../../payment_split';
import type { PaymentStatus } from '../../payment_status';
import { PaymentType } from '../../payment_type';
import { ApplePayPaymentResponseSource } from '../sources/apple_pay/apple_pay_response_source';
Expand Down Expand Up @@ -32,6 +33,7 @@ export class PaymentResponse {
updatedAt: string;
metadata?: Record<string, string | number | boolean> | null;
source: PaymentResponseSource;
splits?: PaymentSplit[] | null;

constructor({
id,
Expand All @@ -56,6 +58,7 @@ export class PaymentResponse {
updatedAt,
metadata,
source,
splits,
}: {
id: string;
status: PaymentStatus;
Expand All @@ -79,6 +82,7 @@ export class PaymentResponse {
updatedAt: string;
metadata?: Record<string, string | number | boolean> | null;
source: PaymentResponseSource;
splits?: PaymentSplit[] | null;
}) {
this.id = id;
this.status = status;
Expand All @@ -102,6 +106,7 @@ export class PaymentResponse {
this.updatedAt = updatedAt;
this.metadata = metadata;
this.source = source;
this.splits = splits;
}

/**
Expand Down Expand Up @@ -153,6 +158,11 @@ export class PaymentResponse {
updatedAt: json.updated_at,
metadata: json.metadata,
source: paymentSource,
splits: json.splits
? (json.splits as Array<Record<string, any>>).map((splitJson) =>
PaymentSplit.fromJson(splitJson)
)
: null,
Comment thread
AbdulazizAlrabiah marked this conversation as resolved.
});
}
}
19 changes: 19 additions & 0 deletions src/models/payment_config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { assert } from '../helpers/assert';
import type { ApplePayConfig } from './apple_pay_config';
import { CreditCardConfig } from './credit_card_config';
import type { PaymentSplit } from './payment_split';
import type { SamsungPayConfig } from './samsung_pay_config';

/**
Expand All @@ -9,6 +10,7 @@ import type { SamsungPayConfig } from './samsung_pay_config';
export class PaymentConfig {
givenId?: string | null;
publishableApiKey: string;
baseUrl: string;
amount: number;
merchantCountryCode: string;
currency: string;
Expand All @@ -20,11 +22,13 @@ export class PaymentConfig {
createSaveOnlyToken: boolean;
samsungPay?: SamsungPayConfig;
applyCoupon?: boolean;
splits?: PaymentSplit[] | null;

/**
* Constructs a new PaymentConfig instance with the provided settings.
* @param givenId - Optional UUID for the payment (UUID v4 is recommended). It will be attached with the payment creation request to support idempotency. `It is going be the ID of the created payment`.
* @param publishableApiKey - Your Moyasar publishable API key - https://docs.moyasar.com/get-your-api-keys.
* @param baseUrl - Moyasar API base URL. Defaults to `https://api.moyasar.com`.
* @param amount - The amount to be charged in the smallest currency unit. For example, to charge `SAR 257.58` you will have the [amount] as `25758`. In other words, 10 SAR = 10 * 100 Halalas. Integer values only.
* @param merchantCountryCode - The country code of the merchant’s principle place of business. Defaults to 'SA'. Must be in ISO 3166-1 alpha-2 country code format.
* @param currency - The currency code for the payment. Defaults to 'SAR'. Must be in ISO 4217 3-letter currency code format.
Expand All @@ -36,10 +40,17 @@ export class PaymentConfig {
* @param createSaveOnlyToken - Optional to process a save only token flow for a Credit Card. Defaults to false - https://docs.moyasar.com/create-token
* @param samsungPay - Required for Samsung Pay feature.
* @param applyCoupon - A flag to control the coupon application (based on the BIN). This key is required only if you don't want to apply the coupon. Otherwise, the coupon is going to be applied. Defaults to true.
* @param splits - Optional array of `PaymentSplit` object used to distribute the charged amount (in the smallest currency unit) among multiple recipients or to collect a platform fee.
* - Each split requires `recipientId` and `amount` parameters.
* - `reference` and `description` parameters are optional.
* - Set `feeSource = true` parameter to mark the split as a fee/commission taken by the platform.
* - Set `refundable` parameter to control whether a split amount is refundable (`true`/`false`). Leave it to use the backend's default.
* - Set the `publishableApiKey` parameter to "pk_test_uQra5pwtUo9GaenMSS4XgfAmeLhmjUTJwFdXJxsH" and set the `baseUrl` parameter to "https://apimig.moyasar.com" for staging testing.
Comment thread
AbdulazizAlrabiah marked this conversation as resolved.
*/
constructor({
givenId,
publishableApiKey,
baseUrl = 'https://api.moyasar.com',
amount,
merchantCountryCode = 'SA',
currency = 'SAR',
Expand All @@ -51,9 +62,11 @@ export class PaymentConfig {
createSaveOnlyToken = false,
samsungPay,
applyCoupon = true,
splits,
}: {
givenId?: string | null;
publishableApiKey: string;
baseUrl?: string;
amount: number;
merchantCountryCode?: string;
currency?: string;
Expand All @@ -65,6 +78,7 @@ export class PaymentConfig {
createSaveOnlyToken?: boolean;
samsungPay?: SamsungPayConfig;
applyCoupon?: boolean;
splits?: PaymentSplit[] | null;
}) {
assert(
publishableApiKey.length > 0,
Expand All @@ -85,9 +99,13 @@ export class PaymentConfig {
supportedNetworks.length > 0,
'At least 1 network must be supported.'
);
assert(baseUrl.length > 0, 'Please fill `baseUrl` argument.');

this.givenId = givenId;
this.publishableApiKey = publishableApiKey;

const cleanedBaseUrl = baseUrl.replace(/\/+$/, ''); // Removes trailing slashes
Comment thread
AbdulazizAlrabiah marked this conversation as resolved.
this.baseUrl = cleanedBaseUrl;
this.amount = amount;
this.merchantCountryCode = merchantCountryCode.toUpperCase();
this.currency = currency;
Expand All @@ -101,5 +119,6 @@ export class PaymentConfig {
this.createSaveOnlyToken = createSaveOnlyToken;
this.samsungPay = samsungPay;
this.applyCoupon = applyCoupon;
this.splits = splits;
}
}
61 changes: 61 additions & 0 deletions src/models/payment_split.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @param splits - Optional array of `PaymentSplit` object used to distribute the charged amount (in the smallest currency unit) among multiple recipients or to collect a platform fee.
* - Each split requires `recipientId` and `amount` parameters.
* - `reference` and `description` parameters are optional.
* - Set `feeSource = true` parameter to mark the split as a fee/commission taken by the platform.
* - Set `refundable` parameter to control whether a split amount is refundable (`true`/`false`). Leave it to use the backend's default.
* - Set the `publishableApiKey` parameter to "pk_test_uQra5pwtUo9GaenMSS4XgfAmeLhmjUTJwFdXJxsH" and set the `baseUrl` parameter to "https://apimig.moyasar.com" for staging testing.
*/
export class PaymentSplit {
public recipientId: string;
public amount: number;
public reference?: string | null;
public description?: string | null;
public feeSource?: boolean | null;
public refundable?: boolean | null;

constructor({
recipientId,
amount,
reference,
description,
feeSource,
refundable,
}: {
recipientId: string;
amount: number;
reference?: string | null;
description?: string | null;
feeSource?: boolean | null;
refundable?: boolean | null;
}) {
this.recipientId = recipientId;
this.amount = amount;
this.reference = reference;
this.description = description;
this.feeSource = feeSource;
this.refundable = refundable;
}

toJson(): Record<string, any> {
return {
recipient_id: this.recipientId,
amount: this.amount,
...(this.reference && { reference: this.reference }),
...(this.description && { description: this.description }),
...(this.feeSource !== undefined && { fee_source: this.feeSource }),
...(this.refundable !== undefined && { refundable: this.refundable }),
Comment thread
AbdulazizAlrabiah marked this conversation as resolved.
};
}

static fromJson(json: Record<string, any>): PaymentSplit {
return new PaymentSplit({
recipientId: json.recipient_id,
amount: json.amount,
reference: json.reference,
description: json.description,
feeSource: json.fee_source,
refundable: json.refundable,
});
}
}
3 changes: 3 additions & 0 deletions src/services/credit_card_payment_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,15 @@ export class CreditCardPaymentService {

const paymentRequest = new PaymentRequest({
givenId: paymentConfig.givenId,
baseUrl: paymentConfig.baseUrl,
amount: paymentConfig.amount,
currency: paymentConfig.currency,
description: paymentConfig.description,
metadata: paymentConfig.metadata,
source: creditCardRequestSource,
callbackUrl: 'https://sdk.moyasar.com/return',
applyCoupon: paymentConfig.applyCoupon,
splits: paymentConfig.splits,
});

const response = await createPayment(
Expand Down Expand Up @@ -156,6 +158,7 @@ export class CreditCardPaymentService {
cvc: creditCardRequestSource.cvc,
month: creditCardRequestSource.month,
year: creditCardRequestSource.year,
baseUrl: paymentConfig.baseUrl,
callbackUrl: 'https://sdk.moyasar.com/return',
metadata: paymentConfig.metadata,
});
Expand Down
Loading
Loading