@coinflowlabs/angular 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # Coinflowlabs
2
+
3
+ This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Run `ng generate component component-name --project coinflowlabs` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project coinflowlabs`.
8
+ > Note: Don't forget to add `--project coinflowlabs` or else it will be added to the default project in your `angular.json` file.
9
+
10
+ ## Build
11
+
12
+ Run `ng build coinflowlabs` to build the project. The build artifacts will be stored in the `dist/` directory.
13
+
14
+ ## Publishing
15
+
16
+ After building your library with `ng build coinflowlabs`, go to the dist folder `cd dist/coinflowlabs` and run `npm publish`.
17
+
18
+ ## Running unit tests
19
+
20
+ Run `ng test coinflowlabs` to execute the unit tests via [Karma](https://karma-runner.github.io).
21
+
22
+ ## Further help
23
+
24
+ To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
File without changes
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/coinflowlabs",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ }
7
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@coinflowlabs/angular",
3
+ "version": "0.0.1",
4
+ "scripts": {
5
+ "clean": "rimraf ../../dist && rimraf ./src/lib/common",
6
+ "build": "npm run codegen && ng build coinflowlabs",
7
+ "codegen": "cp -r ../../../lib-common/src/ ./src/lib/common/"
8
+ },
9
+ "peerDependencies": {
10
+ "@angular/common": "^17.3.0",
11
+ "@angular/core": "^17.3.0",
12
+ "@coinflowlabs/lib-common": "*",
13
+ "@solana/web3.js": ">=1.54.0",
14
+ "bs58": "~5.0.0"
15
+ },
16
+ "dependencies": {
17
+ "tslib": "^2.3.0"
18
+ },
19
+ "peerDependenciesMeta": {
20
+ "@solana/web3.js": {
21
+ "optional": true
22
+ },
23
+ "bs58": {
24
+ "optional": true
25
+ }
26
+ },
27
+ "sideEffects": false
28
+ }
@@ -0,0 +1,63 @@
1
+ import {
2
+ Component,
3
+ ElementRef,
4
+ HostListener,
5
+ Input,
6
+ ViewChild,
7
+ } from '@angular/core';
8
+ import {
9
+ CoinflowIFrameProps,
10
+ CoinflowUtils,
11
+ IFrameMessageHandlers,
12
+ handleIFrameMessage,
13
+ } from './common';
14
+ import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
15
+
16
+ @Component({
17
+ selector: 'lib-coinflow-iframe',
18
+ standalone: true,
19
+ imports: [],
20
+ template: ` <iframe
21
+ width="100%"
22
+ height="100%"
23
+ #iframe
24
+ scrolling="{{ iframeProps?.handleHeightChange ? 'no' : 'yes' }}"
25
+ allow="payment;camera"
26
+ title="withdraw"
27
+ frameBorder="0"
28
+ [src]="dynamicUrl"
29
+ ></iframe>`,
30
+ })
31
+ export class CoinflowIFrameComponent {
32
+ @Input() iframeProps!: CoinflowIFrameProps;
33
+ @Input() messageHandlers!: IFrameMessageHandlers;
34
+
35
+ dynamicUrl?: SafeResourceUrl;
36
+ @ViewChild('iframe') iframe?: ElementRef<HTMLIFrameElement>;
37
+
38
+ constructor(private sanitizer: DomSanitizer) {}
39
+
40
+ ngOnInit() {
41
+ const coinflowUrl = CoinflowUtils.getCoinflowUrl(this.iframeProps);
42
+ this.dynamicUrl =
43
+ this.sanitizer.bypassSecurityTrustResourceUrl(coinflowUrl);
44
+ }
45
+
46
+ @HostListener('window:message', ['$event']) onPostMessage(event: any) {
47
+ if (
48
+ !event.origin.includes(
49
+ CoinflowUtils.getCoinflowBaseUrl(this.iframeProps.env)
50
+ )
51
+ )
52
+ return;
53
+
54
+ const promise = handleIFrameMessage(event.data, this.messageHandlers);
55
+ if (!promise) return;
56
+ promise.then(this.sendMessage.bind(this));
57
+ }
58
+
59
+ sendMessage(message: string) {
60
+ if (!this.iframe || !this.iframe.nativeElement) return;
61
+ this.iframe.nativeElement.contentWindow!.postMessage(message, '*');
62
+ }
63
+ }
@@ -0,0 +1,9 @@
1
+ import {Component} from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'lib-coinflow-purchase-history',
5
+ standalone: true,
6
+ imports: [],
7
+ template: ' <p>coinflow-purchase-history works!</p> ',
8
+ })
9
+ export class CoinflowPurchaseHistoryComponent {}
@@ -0,0 +1,9 @@
1
+ import {Component} from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'lib-coinflow-purchase-protection',
5
+ standalone: true,
6
+ imports: [],
7
+ template: ' <p>coinflow-purchase-protection works!</p> ',
8
+ })
9
+ export class CoinflowPurchaseProtectionComponent {}
@@ -0,0 +1,37 @@
1
+ import {Component, Input} from '@angular/core';
2
+ import {CoinflowIFrameComponent} from './coinflow-iframe.component';
3
+ import {
4
+ CoinflowIFrameProps,
5
+ CoinflowPurchaseProps,
6
+ IFrameMessageHandlers,
7
+ getHandlers,
8
+ getWalletPubkey,
9
+ CoinflowUtils,
10
+ } from './common';
11
+
12
+ @Component({
13
+ selector: 'lib-coinflow-purchase',
14
+ standalone: true,
15
+ imports: [CoinflowIFrameComponent],
16
+ template:
17
+ ' <lib-coinflow-iframe ng-if="iframeProps && messageHandlers" [iframeProps]="iframeProps!" [messageHandlers]="messageHandlers!"></lib-coinflow-iframe> ',
18
+ })
19
+ export class CoinflowPurchaseComponent {
20
+ @Input() purchaseProps!: CoinflowPurchaseProps;
21
+ iframeProps?: CoinflowIFrameProps;
22
+ messageHandlers?: IFrameMessageHandlers;
23
+
24
+ ngOnInit() {
25
+ const walletPubkey = getWalletPubkey(this.purchaseProps);
26
+ this.messageHandlers = getHandlers(this.purchaseProps);
27
+ this.messageHandlers.handleHeightChange =
28
+ this.purchaseProps.handleHeightChange;
29
+
30
+ this.iframeProps = {
31
+ ...this.purchaseProps,
32
+ walletPubkey,
33
+ route: `/purchase/${this.purchaseProps?.merchantId}`,
34
+ transaction: CoinflowUtils.getTransaction(this.purchaseProps),
35
+ };
36
+ }
37
+ }
@@ -0,0 +1,9 @@
1
+ import {Component} from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'lib-coinflow-withdraw-history',
5
+ standalone: true,
6
+ imports: [],
7
+ template: ' <p>coinflow-withdraw-history works!</p> ',
8
+ })
9
+ export class CoinflowWithdrawHistoryComponent {}
@@ -0,0 +1,36 @@
1
+ import {Component, Input} from '@angular/core';
2
+ import {
3
+ CoinflowIFrameProps,
4
+ CoinflowWithdrawProps,
5
+ IFrameMessageHandlers,
6
+ getHandlers,
7
+ getWalletPubkey,
8
+ } from './common';
9
+ import {CoinflowIFrameComponent} from './coinflow-iframe.component';
10
+
11
+ @Component({
12
+ selector: 'lib-coinflow-withdraw',
13
+ standalone: true,
14
+ imports: [CoinflowIFrameComponent],
15
+ template:
16
+ ' <lib-coinflow-iframe ng-if="iframeProps && messageHandlers" [iframeProps]="iframeProps!" [messageHandlers]="messageHandlers!"></lib-coinflow-iframe> ',
17
+ })
18
+ export class CoinflowWithdrawComponent {
19
+ @Input() withdrawProps!: CoinflowWithdrawProps;
20
+ iframeProps?: CoinflowIFrameProps;
21
+ messageHandlers?: IFrameMessageHandlers;
22
+
23
+ ngOnInit() {
24
+ const walletPubkey = getWalletPubkey(this.withdrawProps);
25
+ this.messageHandlers = getHandlers(this.withdrawProps);
26
+ this.messageHandlers.handleHeightChange =
27
+ this.withdrawProps.handleHeightChange;
28
+
29
+ this.iframeProps = {
30
+ ...this.withdrawProps,
31
+ walletPubkey,
32
+ route: `/withdraw/${this.withdrawProps?.merchantId}`,
33
+ transaction: undefined,
34
+ };
35
+ }
36
+ }
@@ -0,0 +1,188 @@
1
+ import {
2
+ CoinflowPurchaseProps,
3
+ EthWallet,
4
+ NearWallet,
5
+ SolanaWallet,
6
+ } from './CoinflowTypes';
7
+ import {CoinflowUtils} from './CoinflowUtils';
8
+ import {Transaction, VersionedTransaction} from '@solana/web3.js';
9
+ import * as web3 from '@solana/web3.js';
10
+ import base58 from 'bs58';
11
+
12
+ export type WalletCall = {method: IFrameMessageMethods; data: string};
13
+
14
+ export interface IFrameMessageHandlers {
15
+ handleSendTransaction: (transaction: string) => Promise<string>;
16
+ handleSignMessage?: (message: string) => Promise<string>;
17
+ handleSignTransaction?: (transaction: string) => Promise<string>;
18
+ handleHeightChange?: (height: string) => void;
19
+ }
20
+
21
+ enum IFrameMessageMethods {
22
+ SignMessage = 'signMessage',
23
+ SignTransaction = 'signTransaction',
24
+ SendTransaction = 'sendTransaction',
25
+ HeightChange = 'heightChange',
26
+ }
27
+
28
+ export function getWalletPubkey({
29
+ wallet,
30
+ }: Pick<CoinflowPurchaseProps, 'wallet'>): string | null | undefined {
31
+ if ('publicKey' in wallet) {
32
+ return wallet.publicKey!.toString();
33
+ }
34
+
35
+ if ('address' in wallet) {
36
+ return wallet.address;
37
+ }
38
+
39
+ if ('accountId' in wallet) {
40
+ return wallet.accountId;
41
+ }
42
+
43
+ return null;
44
+ }
45
+
46
+ export function handleIFrameMessage(
47
+ rawMessage: string,
48
+ handlers: IFrameMessageHandlers
49
+ ): Promise<string> | void {
50
+ let walletCall: WalletCall;
51
+ try {
52
+ walletCall = JSON.parse(rawMessage);
53
+ if (!('method' in walletCall) || !('data' in walletCall)) return;
54
+ } catch (e) {
55
+ console.error('handleIFrameMessage JSON parse', e);
56
+ return;
57
+ }
58
+
59
+ const {data, method} = walletCall;
60
+ switch (method) {
61
+ case IFrameMessageMethods.SignMessage:
62
+ if (!handlers.handleSignMessage) return;
63
+ return handlers.handleSignMessage(data);
64
+ case IFrameMessageMethods.SignTransaction:
65
+ if (!handlers.handleSignTransaction) return;
66
+ return handlers.handleSignTransaction(data);
67
+ case IFrameMessageMethods.SendTransaction:
68
+ return handlers.handleSendTransaction(data);
69
+ case IFrameMessageMethods.HeightChange:
70
+ if (!handlers.handleHeightChange) return;
71
+ return handlers.handleHeightChange(data);
72
+ }
73
+
74
+ console.warn(
75
+ `Didn't expect to get here, handleIFrameMessage method:${method} is not one of ${Object.values(IFrameMessageMethods)}`
76
+ );
77
+ }
78
+
79
+ export function getHandlers({
80
+ wallet,
81
+ blockchain,
82
+ }: Pick<CoinflowPurchaseProps, 'wallet' | 'blockchain'>): Omit<
83
+ IFrameMessageHandlers,
84
+ 'handleHeightChange'
85
+ > {
86
+ return CoinflowUtils.byBlockchain(blockchain, {
87
+ solana: () => getSolanaWalletHandlers({wallet}),
88
+ near: () => getNearWalletHandlers({wallet}),
89
+ eth: () => getEvmWalletHandlers({wallet}),
90
+ polygon: () => getEvmWalletHandlers({wallet}),
91
+ base: () => getEvmWalletHandlers({wallet}),
92
+ })();
93
+ }
94
+
95
+ function getSolanaWalletHandlers({
96
+ wallet,
97
+ }: Pick<CoinflowPurchaseProps, 'wallet'>): Omit<
98
+ IFrameMessageHandlers,
99
+ 'handleHeightChange'
100
+ > {
101
+ return {
102
+ handleSendTransaction: async (transaction: string) => {
103
+ const tx = getSolanaTransaction(transaction);
104
+ return (wallet as SolanaWallet).sendTransaction(tx);
105
+ },
106
+ handleSignMessage: async (message: string) => {
107
+ const signMessage = (wallet as SolanaWallet).signMessage;
108
+ if (!signMessage) {
109
+ throw new Error('signMessage is not supported by this wallet');
110
+ }
111
+
112
+ const signedMessage = await signMessage(
113
+ new TextEncoder().encode(message)
114
+ );
115
+ return base58.encode(signedMessage);
116
+ },
117
+ handleSignTransaction: async (transaction: string) => {
118
+ const signTransaction = (wallet as SolanaWallet).signTransaction;
119
+ if (!signTransaction) {
120
+ throw new Error('signTransaction is not supported by this wallet');
121
+ }
122
+ const tx = getSolanaTransaction(transaction);
123
+ const signedTransaction = await signTransaction(tx);
124
+ return base58.encode(
125
+ signedTransaction.serialize({
126
+ requireAllSignatures: false,
127
+ verifySignatures: false,
128
+ })
129
+ );
130
+ },
131
+ };
132
+ }
133
+
134
+ function getSolanaTransaction(
135
+ data: string
136
+ ): Transaction | VersionedTransaction {
137
+ if (!web3)
138
+ throw new Error(
139
+ '@solana/web3.js is not defined. Please install @solana/web3.js into your project'
140
+ );
141
+
142
+ if (!base58)
143
+ throw new Error(
144
+ 'bs58 is not defined. Please install bs58 into your project'
145
+ );
146
+
147
+ const parsedUInt8Array = base58.decode(data);
148
+ const vtx = web3.VersionedTransaction.deserialize(parsedUInt8Array);
149
+ if (vtx.version === 'legacy') return web3.Transaction.from(parsedUInt8Array);
150
+ return vtx;
151
+ }
152
+
153
+ function getNearWalletHandlers({
154
+ wallet,
155
+ }: Pick<CoinflowPurchaseProps, 'wallet'>): Omit<
156
+ IFrameMessageHandlers,
157
+ 'handleHeightChange'
158
+ > {
159
+ const nearWallet = wallet as NearWallet;
160
+ return {
161
+ handleSendTransaction: async (transaction: string) => {
162
+ const action = JSON.parse(Buffer.from(transaction, 'base64').toString());
163
+ const executionOutcome = await nearWallet.signAndSendTransaction(action);
164
+ if (!executionOutcome) throw new Error('Transaction did not send');
165
+ const {transaction: transactionResult} = executionOutcome;
166
+ return transactionResult.hash;
167
+ },
168
+ };
169
+ }
170
+
171
+ function getEvmWalletHandlers({
172
+ wallet,
173
+ }: Pick<CoinflowPurchaseProps, 'wallet'>): Omit<
174
+ IFrameMessageHandlers,
175
+ 'handleHeightChange'
176
+ > {
177
+ const evmWallet = wallet as EthWallet;
178
+ return {
179
+ handleSendTransaction: async (transaction: string) => {
180
+ const tx = JSON.parse(Buffer.from(transaction, 'base64').toString());
181
+ const {hash} = await evmWallet.sendTransaction(tx);
182
+ return hash;
183
+ },
184
+ handleSignMessage: async (message: string) => {
185
+ return evmWallet.signMessage(message);
186
+ },
187
+ };
188
+ }
@@ -0,0 +1,398 @@
1
+ import type {Connection, VersionedTransaction} from '@solana/web3.js';
2
+ import {PublicKey, Signer, Transaction} from '@solana/web3.js';
3
+
4
+ export enum SettlementType {
5
+ Credits = 'Credits',
6
+ USDC = 'USDC',
7
+ Bank = 'Bank',
8
+ }
9
+
10
+ export enum MerchantStyle {
11
+ Rounded = 'rounded',
12
+ Sharp = 'sharp',
13
+ Pill = 'pill',
14
+ }
15
+
16
+ export type MerchantTheme = {
17
+ primary?: string;
18
+ background?: string;
19
+ backgroundAccent?: string;
20
+ backgroundAccent2?: string;
21
+ textColor?: string;
22
+ textColorAccent?: string;
23
+ textColorAction?: string;
24
+ font?: string;
25
+ style?: MerchantStyle;
26
+ };
27
+
28
+ export interface CustomerInfo {
29
+ name?: string;
30
+ verificationId?: string;
31
+ displayName?: string;
32
+ address?: string;
33
+ city?: string;
34
+ state?: string;
35
+ zip?: string;
36
+ country?: string;
37
+ ip?: string;
38
+ lat?: string;
39
+ lng?: string;
40
+ }
41
+
42
+ /** Coinflow Types **/
43
+ export type CoinflowBlockchain = 'solana' | 'near' | 'eth' | 'polygon' | 'base';
44
+ export type CoinflowEnvs = 'prod' | 'staging' | 'sandbox' | 'local';
45
+
46
+ export interface CoinflowTypes {
47
+ merchantId: string;
48
+ env?: CoinflowEnvs;
49
+ loaderBackground?: string;
50
+ blockchain: CoinflowBlockchain;
51
+ handleHeightChange?: (height: string) => void;
52
+ theme?: MerchantTheme;
53
+ }
54
+
55
+ export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
56
+
57
+ export type OnSuccessMethod = (params: string) => void | Promise<void>;
58
+
59
+ /** Wallets **/
60
+ export interface SolanaWallet {
61
+ publicKey: PublicKey | null;
62
+ signTransaction?: <T extends Transaction | VersionedTransaction>(
63
+ transaction: T
64
+ ) => Promise<T>;
65
+ sendTransaction: <T extends Transaction | VersionedTransaction>(
66
+ transaction: T
67
+ ) => Promise<string>;
68
+ signMessage?: (message: Uint8Array) => Promise<Uint8Array>;
69
+ }
70
+
71
+ export interface NearWallet {
72
+ accountId: string;
73
+ signAndSendTransaction: (transaction: unknown) => Promise<{
74
+ transaction: {hash: string};
75
+ }>;
76
+ }
77
+
78
+ type AccessList = Array<{address: string; storageKeys: Array<string>}>;
79
+ type AccessListish =
80
+ | AccessList
81
+ | Array<[string, Array<string>]>
82
+ | Record<string, Array<string>>;
83
+
84
+ export type EthWallet = {
85
+ address: string | null | undefined;
86
+ sendTransaction: (transaction: {
87
+ to: string;
88
+ from?: string;
89
+ nonce?: Bytes | bigint | string | number;
90
+
91
+ gasLimit?: Bytes | bigint | string | number;
92
+ gasPrice?: Bytes | bigint | string | number;
93
+
94
+ data?: BytesLike;
95
+ value?: Bytes | bigint | string | number;
96
+ chainId?: number;
97
+
98
+ type?: number;
99
+ accessList?: AccessListish;
100
+
101
+ maxPriorityFeePerGas?: Bytes | bigint | string | number;
102
+ maxFeePerGas?: Bytes | bigint | string | number;
103
+
104
+ customData?: Record<string, any>;
105
+ ccipReadEnabled?: boolean;
106
+ }) => Promise<{hash: string}>;
107
+ signMessage: (message: string) => Promise<string>;
108
+ };
109
+
110
+ /** History **/
111
+ export interface CoinflowSolanaHistoryProps extends CoinflowTypes {
112
+ wallet: SolanaWallet;
113
+ connection: Connection;
114
+ blockchain: 'solana';
115
+ }
116
+
117
+ export interface CoinflowNearHistoryProps extends CoinflowTypes {
118
+ wallet: NearWallet;
119
+ blockchain: 'near';
120
+ }
121
+
122
+ export interface CoinflowEvmHistoryProps extends CoinflowTypes {
123
+ wallet: EthWallet;
124
+ }
125
+
126
+ export interface CoinflowEthHistoryProps extends CoinflowEvmHistoryProps {
127
+ blockchain: 'eth';
128
+ }
129
+
130
+ export interface CoinflowPolygonHistoryProps extends CoinflowEvmHistoryProps {
131
+ blockchain: 'polygon';
132
+ }
133
+
134
+ export interface CoinflowBaseHistoryProps extends CoinflowEvmHistoryProps {
135
+ blockchain: 'base';
136
+ }
137
+
138
+ export type CoinflowHistoryProps =
139
+ | CoinflowSolanaHistoryProps
140
+ | CoinflowNearHistoryProps
141
+ | CoinflowPolygonHistoryProps
142
+ | CoinflowEthHistoryProps
143
+ | CoinflowBaseHistoryProps;
144
+
145
+ /** Transactions **/
146
+
147
+ export type NearFtTransferCallAction = {
148
+ methodName: 'ft_transfer_call';
149
+ args: object;
150
+ gas: string;
151
+ deposit: string;
152
+ };
153
+
154
+ type Bytes = ArrayLike<number>;
155
+ type BytesLike = Bytes | string;
156
+
157
+ /** Purchase **/
158
+
159
+ export type ChargebackProtectionData = ChargebackProtectionItem[];
160
+
161
+ export interface ChargebackProtectionItem {
162
+ /**
163
+ * The name of the product
164
+ */
165
+ productName: string;
166
+ /**
167
+ * The product type. Possible values include: inGameProduct, gameOfSkill, dataStorage, computingResources, sportsTicket, eSportsTicket, musicTicket, conferenceTicket, virtualSportsTicket, virtualESportsTicket, virtualMusicTicket, virtualConferenceTicket, alcohol, DLC, subscription, fundACause, realEstate, computingContract, digitalArt, topUp
168
+ */
169
+ productType:
170
+ | 'inGameProduct'
171
+ | 'gameOfSkill'
172
+ | 'dataStorage'
173
+ | 'computingResources'
174
+ | 'sportsTicket'
175
+ | 'eSportsTicket'
176
+ | 'musicTicket'
177
+ | 'conferenceTicket'
178
+ | 'virtualSportsTicket'
179
+ | 'virtualESportsTicket'
180
+ | 'virtualMusicTicket'
181
+ | 'virtualConferenceTicket'
182
+ | 'alcohol'
183
+ | 'DLC'
184
+ | 'subscription'
185
+ | 'fundACause'
186
+ | 'realEstate'
187
+ | 'computingContract'
188
+ | 'digitalArt'
189
+ | 'topUp'
190
+ | 'ownershipContract';
191
+ /**
192
+ * The item's list price
193
+ */
194
+ /**
195
+ * The number of units sold
196
+ */
197
+ quantity: number;
198
+ /**
199
+ * Any additional data that the store can provide on the product, e.g. description, link to image, etc.
200
+ */
201
+ rawProductData?: {[key: string]: any};
202
+ }
203
+
204
+ export interface CoinflowCommonPurchaseProps extends CoinflowTypes {
205
+ amount?: number;
206
+ onSuccess?: OnSuccessMethod;
207
+ webhookInfo?: object;
208
+ email?: string;
209
+ chargebackProtectionData?: ChargebackProtectionData;
210
+ planCode?: string;
211
+ disableApplePay?: boolean;
212
+ disableGooglePay?: boolean;
213
+ customerInfo?: CustomerInfo;
214
+ settlementType?: SettlementType;
215
+ authOnly?: boolean;
216
+ deviceId?: string;
217
+ }
218
+
219
+ export interface CoinflowSolanaPurchaseProps
220
+ extends CoinflowCommonPurchaseProps {
221
+ wallet: SolanaWallet;
222
+ transaction?: Transaction | VersionedTransaction;
223
+ partialSigners?: Signer[];
224
+ debugTx?: boolean;
225
+ connection: Connection;
226
+ blockchain: 'solana';
227
+ token?: PublicKey | string;
228
+ supportsVersionedTransactions?: boolean;
229
+ rent?: {lamports: string | number};
230
+ nativeSolToConvert?: {lamports: string | number};
231
+ }
232
+
233
+ export interface CoinflowNearPurchaseProps extends CoinflowCommonPurchaseProps {
234
+ wallet: NearWallet;
235
+ blockchain: 'near';
236
+ action?: NearFtTransferCallAction;
237
+ nearDeposit?: string;
238
+ }
239
+
240
+ export interface CoinflowEvmPurchaseProps extends CoinflowCommonPurchaseProps {
241
+ transaction?: EvmTransactionData;
242
+ token?: string;
243
+ wallet: EthWallet;
244
+ }
245
+
246
+ export interface CoinflowPolygonPurchaseProps extends CoinflowEvmPurchaseProps {
247
+ blockchain: 'polygon';
248
+ }
249
+
250
+ export interface CoinflowEthPurchaseProps extends CoinflowEvmPurchaseProps {
251
+ blockchain: 'eth';
252
+ }
253
+
254
+ export interface CoinflowBasePurchaseProps extends CoinflowEvmPurchaseProps {
255
+ blockchain: 'base';
256
+ }
257
+
258
+ export type CoinflowPurchaseProps =
259
+ | CoinflowSolanaPurchaseProps
260
+ | CoinflowNearPurchaseProps
261
+ | CoinflowPolygonPurchaseProps
262
+ | CoinflowEthPurchaseProps
263
+ | CoinflowBasePurchaseProps;
264
+
265
+ /** Withdraw **/
266
+
267
+ export interface CoinflowCommonWithdrawProps extends CoinflowTypes {
268
+ onSuccess?: OnSuccessMethod;
269
+ tokens?: string[];
270
+ lockDefaultToken?: boolean;
271
+ amount?: number;
272
+ email?: string;
273
+ bankAccountLinkRedirect?: string;
274
+ additionalWallets?: {
275
+ wallet: string;
276
+ blockchain: 'solana' | 'eth' | 'near' | 'polygon';
277
+ }[];
278
+ supportsVersionedTransactions?: boolean;
279
+ lockAmount?: boolean;
280
+ transactionSigner?: string;
281
+ }
282
+
283
+ export interface CoinflowSolanaWithdrawProps
284
+ extends CoinflowCommonWithdrawProps {
285
+ wallet: SolanaWallet;
286
+ connection: Connection;
287
+ blockchain: 'solana';
288
+ }
289
+
290
+ export interface CoinflowNearWithdrawProps extends CoinflowCommonWithdrawProps {
291
+ wallet: NearWallet;
292
+ blockchain: 'near';
293
+ }
294
+
295
+ export interface CoinflowEvmWithdrawProps extends CoinflowCommonWithdrawProps {
296
+ wallet: EthWallet;
297
+ usePermit?: boolean;
298
+ }
299
+
300
+ export interface CoinflowEthWithdrawProps extends CoinflowEvmWithdrawProps {
301
+ blockchain: 'eth';
302
+ usePermit?: boolean;
303
+ }
304
+
305
+ export interface CoinflowPolygonWithdrawProps extends CoinflowEvmWithdrawProps {
306
+ blockchain: 'polygon';
307
+ }
308
+
309
+ export interface CoinflowBaseWithdrawProps extends CoinflowEvmWithdrawProps {
310
+ blockchain: 'base';
311
+ }
312
+
313
+ export type CoinflowWithdrawProps =
314
+ | CoinflowSolanaWithdrawProps
315
+ | CoinflowNearWithdrawProps
316
+ | CoinflowEthWithdrawProps
317
+ | CoinflowPolygonWithdrawProps
318
+ | CoinflowBaseWithdrawProps;
319
+
320
+ export interface CommonEvmRedeem {
321
+ waitForHash?: boolean;
322
+ }
323
+
324
+ export interface NormalRedeem extends CommonEvmRedeem {
325
+ transaction: {to: string; data: string};
326
+ }
327
+
328
+ export interface KnownTokenIdRedeem extends NormalRedeem {
329
+ nftContract: string;
330
+ nftId: string;
331
+ }
332
+
333
+ export interface SafeMintRedeem extends NormalRedeem {
334
+ type: 'safeMint';
335
+ nftContract?: string;
336
+ }
337
+
338
+ export interface ReturnedTokenIdRedeem extends NormalRedeem {
339
+ type: 'returned';
340
+ nftContract?: string;
341
+ }
342
+
343
+ type ReservoirNftIdItem = Omit<KnownTokenIdRedeem, keyof NormalRedeem>;
344
+ interface ReservoirOrderIdItem {
345
+ orderId: string;
346
+ }
347
+ type ReservoirItem = ReservoirNftIdItem | ReservoirOrderIdItem;
348
+ type ReservoirItems = ReservoirItem | ReservoirItem[];
349
+
350
+ export interface ReservoirRedeem extends CommonEvmRedeem {
351
+ type: 'reservoir';
352
+ items: ReservoirItems;
353
+ }
354
+
355
+ export type EvmTransactionData =
356
+ | SafeMintRedeem
357
+ | ReturnedTokenIdRedeem
358
+ | ReservoirRedeem
359
+ | KnownTokenIdRedeem
360
+ | NormalRedeem;
361
+
362
+ export interface CoinflowIFrameProps
363
+ extends Omit<CoinflowTypes, 'merchantId'>,
364
+ Pick<
365
+ CoinflowCommonPurchaseProps,
366
+ | 'chargebackProtectionData'
367
+ | 'webhookInfo'
368
+ | 'amount'
369
+ | 'customerInfo'
370
+ | 'settlementType'
371
+ | 'email'
372
+ | 'planCode'
373
+ | 'deviceId'
374
+ >,
375
+ Pick<
376
+ CoinflowCommonWithdrawProps,
377
+ | 'bankAccountLinkRedirect'
378
+ | 'additionalWallets'
379
+ | 'transactionSigner'
380
+ | 'lockAmount'
381
+ | 'lockDefaultToken'
382
+ >,
383
+ Pick<CoinflowEvmPurchaseProps, 'authOnly'>,
384
+ Pick<CoinflowSolanaPurchaseProps, 'rent' | 'nativeSolToConvert' | 'token'> {
385
+ walletPubkey: string | null | undefined;
386
+ route: string;
387
+ routePrefix?: string;
388
+ transaction?: string;
389
+ tokens?: string[] | PublicKey[];
390
+ supportsVersionedTransactions?: boolean;
391
+ nearDeposit?: string;
392
+ merchantCss?: string;
393
+ color?: 'white' | 'black';
394
+ disableApplePay?: boolean;
395
+ disableGooglePay?: boolean;
396
+ theme?: MerchantTheme;
397
+ usePermit?: boolean;
398
+ }
@@ -0,0 +1,267 @@
1
+ import {
2
+ CoinflowBlockchain,
3
+ CoinflowEnvs,
4
+ CoinflowIFrameProps,
5
+ CoinflowPurchaseProps,
6
+ } from './CoinflowTypes';
7
+ import {Transaction, VersionedTransaction} from '@solana/web3.js';
8
+ import base58 from 'bs58';
9
+
10
+ export class CoinflowUtils {
11
+ env: CoinflowEnvs;
12
+ url: string;
13
+
14
+ constructor(env?: CoinflowEnvs) {
15
+ this.env = env ?? 'prod';
16
+ if (this.env === 'prod') this.url = 'https://api.coinflow.cash';
17
+ else if (this.env === 'local') this.url = 'http://localhost:5000';
18
+ else this.url = `https://api-${this.env}.coinflow.cash`;
19
+ }
20
+
21
+ async getNSurePartnerId(merchantId: string): Promise<string | undefined> {
22
+ return fetch(this.url + `/merchant/view/${merchantId}`)
23
+ .then(response => response.json())
24
+ .then(
25
+ (json: {
26
+ nSurePartnerId: string | undefined;
27
+ nSureSettings: {nSurePartnerId: string | undefined};
28
+ }) => json.nSureSettings?.nSurePartnerId || json.nSurePartnerId
29
+ )
30
+ .catch(e => {
31
+ console.error(e);
32
+ return undefined;
33
+ });
34
+ }
35
+
36
+ async getCreditBalance(
37
+ publicKey: string,
38
+ merchantId: string,
39
+ blockchain: 'solana' | 'near'
40
+ ): Promise<{cents: number}> {
41
+ const response = await fetch(
42
+ this.url + `/api/customer/balances/${merchantId}`,
43
+ {
44
+ method: 'GET',
45
+ headers: {
46
+ 'x-coinflow-auth-wallet': publicKey,
47
+ 'x-coinflow-auth-blockchain': blockchain,
48
+ },
49
+ }
50
+ );
51
+ const {credits} = await response.json();
52
+ return credits;
53
+ }
54
+
55
+ static getCoinflowBaseUrl(env?: CoinflowEnvs): string {
56
+ if (!env || env === 'prod') return 'https://coinflow.cash';
57
+ if (env === 'local') return 'http://localhost:3000';
58
+
59
+ return `https://${env}.coinflow.cash`;
60
+ }
61
+
62
+ static getCoinflowApiUrl(env?: CoinflowEnvs): string {
63
+ if (!env || env === 'prod') return 'https://api.coinflow.cash';
64
+ if (env === 'local') return 'http://localhost:5000';
65
+
66
+ return `https://api-${env}.coinflow.cash`;
67
+ }
68
+
69
+ static getCoinflowUrl({
70
+ walletPubkey,
71
+ route,
72
+ routePrefix,
73
+ env,
74
+ amount,
75
+ transaction,
76
+ blockchain,
77
+ supportsVersionedTransactions,
78
+ webhookInfo,
79
+ email,
80
+ loaderBackground,
81
+ handleHeightChange,
82
+ bankAccountLinkRedirect,
83
+ additionalWallets,
84
+ nearDeposit,
85
+ chargebackProtectionData,
86
+ merchantCss,
87
+ color,
88
+ rent,
89
+ lockDefaultToken,
90
+ token,
91
+ tokens,
92
+ planCode,
93
+ disableApplePay,
94
+ disableGooglePay,
95
+ customerInfo,
96
+ settlementType,
97
+ lockAmount,
98
+ nativeSolToConvert,
99
+ theme,
100
+ usePermit,
101
+ transactionSigner,
102
+ authOnly,
103
+ deviceId,
104
+ }: CoinflowIFrameProps): string {
105
+ const prefix = routePrefix
106
+ ? `/${routePrefix}/${blockchain}`
107
+ : `/${blockchain}`;
108
+ const url = new URL(prefix + route, CoinflowUtils.getCoinflowBaseUrl(env));
109
+ url.searchParams.append('pubkey', walletPubkey!);
110
+
111
+ if (transaction) {
112
+ url.searchParams.append('transaction', transaction);
113
+ }
114
+ if (amount) {
115
+ url.searchParams.append('amount', amount.toString());
116
+ }
117
+
118
+ if (supportsVersionedTransactions) {
119
+ url.searchParams.append('supportsVersionedTransactions', 'true');
120
+ }
121
+
122
+ if (webhookInfo) {
123
+ url.searchParams.append(
124
+ 'webhookInfo',
125
+ Buffer.from(JSON.stringify(webhookInfo)).toString('base64')
126
+ );
127
+ }
128
+
129
+ if (theme) {
130
+ url.searchParams.append(
131
+ 'theme',
132
+ Buffer.from(JSON.stringify(theme)).toString('base64')
133
+ );
134
+ }
135
+
136
+ if (customerInfo) {
137
+ url.searchParams.append(
138
+ 'customerInfo',
139
+ Buffer.from(JSON.stringify(customerInfo)).toString('base64')
140
+ );
141
+ }
142
+
143
+ if (email) {
144
+ url.searchParams.append('email', email);
145
+ }
146
+
147
+ if (token) {
148
+ url.searchParams.append('token', token.toString());
149
+ }
150
+
151
+ if (tokens) {
152
+ url.searchParams.append('tokens', tokens.toString());
153
+ }
154
+
155
+ if (loaderBackground) {
156
+ url.searchParams.append('loaderBackground', loaderBackground);
157
+ }
158
+
159
+ if (handleHeightChange) {
160
+ url.searchParams.append('useHeightChange', 'true');
161
+ }
162
+
163
+ if (bankAccountLinkRedirect) {
164
+ url.searchParams.append(
165
+ 'bankAccountLinkRedirect',
166
+ bankAccountLinkRedirect
167
+ );
168
+ }
169
+
170
+ if (additionalWallets)
171
+ url.searchParams.append(
172
+ 'additionalWallets',
173
+ JSON.stringify(additionalWallets)
174
+ );
175
+
176
+ if (nearDeposit) url.searchParams.append('nearDeposit', nearDeposit);
177
+
178
+ if (chargebackProtectionData)
179
+ url.searchParams.append(
180
+ 'chargebackProtectionData',
181
+ JSON.stringify(chargebackProtectionData)
182
+ );
183
+ if (deviceId) {
184
+ url.searchParams.append('deviceId', deviceId);
185
+ } else {
186
+ if (typeof window !== 'undefined') {
187
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
188
+ // @ts-ignore
189
+ const deviceId = window?.nSureSDK?.getDeviceId();
190
+ if (deviceId) url.searchParams.append('deviceId', deviceId);
191
+ }
192
+ }
193
+
194
+ if (merchantCss) url.searchParams.append('merchantCss', merchantCss);
195
+ if (color) url.searchParams.append('color', color);
196
+ if (rent) url.searchParams.append('rent', rent.lamports.toString());
197
+ if (nativeSolToConvert)
198
+ url.searchParams.append(
199
+ 'nativeSolToConvert',
200
+ nativeSolToConvert.lamports.toString()
201
+ );
202
+ if (lockDefaultToken) url.searchParams.append('lockDefaultToken', 'true');
203
+ if (planCode) url.searchParams.append('planCode', planCode);
204
+
205
+ if (disableApplePay) url.searchParams.append('disableApplePay', 'true');
206
+ if (disableGooglePay) url.searchParams.append('disableGooglePay', 'true');
207
+ if (settlementType)
208
+ url.searchParams.append('settlementType', settlementType);
209
+
210
+ if (lockAmount) url.searchParams.append('lockAmount', 'true');
211
+
212
+ if (usePermit === false) url.searchParams.append('usePermit', 'false');
213
+ if (transactionSigner)
214
+ url.searchParams.append('transactionSigner', transactionSigner);
215
+ if (authOnly === true) url.searchParams.append('authOnly', 'true');
216
+
217
+ return url.toString();
218
+ }
219
+
220
+ static getTransaction(props: CoinflowPurchaseProps): string | undefined {
221
+ if ('transaction' in props) {
222
+ const {transaction} = props;
223
+ if (transaction instanceof Transaction) {
224
+ return base58.encode(
225
+ transaction.serialize({
226
+ requireAllSignatures: false,
227
+ verifySignatures: false,
228
+ })
229
+ );
230
+ }
231
+
232
+ if (transaction instanceof VersionedTransaction) {
233
+ return base58.encode(transaction.serialize());
234
+ }
235
+
236
+ return btoa(JSON.stringify(transaction));
237
+ }
238
+
239
+ if ('action' in props) {
240
+ return btoa(JSON.stringify(props.action));
241
+ }
242
+
243
+ return undefined;
244
+ }
245
+
246
+ static byBlockchain<T>(
247
+ blockchain: CoinflowBlockchain,
248
+ args: {solana: T; near: T; eth?: T; polygon: T; base: T}
249
+ ): T {
250
+ switch (blockchain) {
251
+ case 'solana':
252
+ return args.solana;
253
+ case 'near':
254
+ return args.near;
255
+ case 'polygon':
256
+ return args.polygon;
257
+ case 'eth':
258
+ if (args.eth === undefined)
259
+ throw new Error('blockchain not supported for this operation!');
260
+ return args.eth;
261
+ case 'base':
262
+ return args.base;
263
+ default:
264
+ throw new Error('blockchain not supported!');
265
+ }
266
+ }
267
+ }
@@ -0,0 +1,3 @@
1
+ export * from './CoinflowTypes';
2
+ export * from './CoinflowUtils';
3
+ export * from './CoinflowLibMessageHandlers';
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Public API Surface of coinflowlabs
3
+ */
4
+
5
+ export * from './lib/coinflow-iframe.component';
6
+ export * from './lib/coinflow-purchase.component';
7
+ export * from './lib/coinflow-purchase-history.component';
8
+ export * from './lib/coinflow-purchase-protection.component';
9
+ export * from './lib/coinflow-withdraw.component';
10
+ export * from './lib/coinflow-withdraw-history.component';
@@ -0,0 +1,14 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../out-tsc/lib",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "inlineSources": true,
9
+ "types": []
10
+ },
11
+ "exclude": [
12
+ "**/*.spec.ts"
13
+ ]
14
+ }
@@ -0,0 +1,10 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "./tsconfig.lib.json",
4
+ "compilerOptions": {
5
+ "declarationMap": false
6
+ },
7
+ "angularCompilerOptions": {
8
+ "compilationMode": "partial"
9
+ }
10
+ }
@@ -0,0 +1,14 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../out-tsc/spec",
6
+ "types": [
7
+ "jasmine"
8
+ ]
9
+ },
10
+ "include": [
11
+ "**/*.spec.ts",
12
+ "**/*.d.ts"
13
+ ]
14
+ }