@bloque/payments-core 0.0.7 → 0.0.8

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,41 @@
1
+ # @bloque/payments-core
2
+
3
+ Core JavaScript library for integrating Bloque hosted checkout into any web application.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm install @bloque/payments-core
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```javascript
14
+ import { createCheckout } from '@bloque/payments-core';
15
+
16
+ // First, create a payment intent using @bloque/payments on your backend
17
+ // Then use the checkout_id to mount the checkout
18
+ const checkout = createCheckout({
19
+ checkoutId: 'checkout_123abc',
20
+ container: '#checkout-container',
21
+ onSuccess: (data) => {
22
+ console.log('Payment successful!', data);
23
+ },
24
+ onError: (error) => {
25
+ console.error('Payment failed:', error);
26
+ },
27
+ });
28
+
29
+ // Later, cleanup
30
+ checkout.destroy();
31
+ ```
32
+
33
+ ## Documentation
34
+
35
+ For complete documentation, examples, and API reference, visit:
36
+
37
+ **[https://docs.bloque.app/pay](https://docs.bloque.app/pay)**
38
+
39
+ ## License
40
+
41
+ [MIT](../../LICENSE)
@@ -0,0 +1,54 @@
1
+ import type { BloqueCheckoutOptions, BloqueInitOptions } from './types';
2
+ export declare class BloqueCheckout {
3
+ private static globalConfig;
4
+ private iframe;
5
+ private options;
6
+ private messageListener;
7
+ private isReady;
8
+ /**
9
+ * Initialize global configuration for all BloqueCheckout instances
10
+ * This allows you to set publicApiKey and mode once instead of passing them to every checkout
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * BloqueCheckout.init({
15
+ * publicApiKey: 'pk_test_...',
16
+ * mode: 'sandbox'
17
+ * });
18
+ * ```
19
+ */
20
+ static init(config: BloqueInitOptions): void;
21
+ static getGlobalConfig(): BloqueInitOptions | null;
22
+ constructor(options: BloqueCheckoutOptions);
23
+ createIframe(): HTMLIFrameElement;
24
+ mount(container: HTMLElement | string): void;
25
+ private setupMessageListener;
26
+ private handleCheckoutReady;
27
+ private handlePaymentResult;
28
+ private handlePaymentError;
29
+ isCheckoutReady(): boolean;
30
+ getIframe(): HTMLIFrameElement | null;
31
+ updateOptions(options: Partial<BloqueCheckoutOptions>): void;
32
+ destroy(): void;
33
+ }
34
+ /**
35
+ * Initialize global configuration for all BloqueCheckout instances
36
+ * This is a convenience wrapper around BloqueCheckout.init()
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { init } from '@bloque/payments-core';
41
+ *
42
+ * init({
43
+ * publicApiKey: 'pk_test_...',
44
+ * mode: 'sandbox'
45
+ * });
46
+ * ```
47
+ */
48
+ export declare function init(config: BloqueInitOptions): void;
49
+ /**
50
+ * Helper function to create and mount a checkout in one step
51
+ */
52
+ export declare function createCheckout(options: BloqueCheckoutOptions & {
53
+ container: HTMLElement | string;
54
+ }): BloqueCheckout;
package/dist/index.cjs CHANGED
@@ -1,5 +1,16 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
3
14
  (()=>{
4
15
  __webpack_require__.r = (exports1)=>{
5
16
  if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
@@ -12,7 +23,159 @@ var __webpack_require__ = {};
12
23
  })();
13
24
  var __webpack_exports__ = {};
14
25
  __webpack_require__.r(__webpack_exports__);
15
- for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ BloqueCheckout: ()=>BloqueCheckout,
28
+ createCheckout: ()=>createCheckout,
29
+ init: ()=>init
30
+ });
31
+ const DEFAULT_CHECKOUT_URL = 'http://payments.bloque.app/checkout';
32
+ class BloqueCheckout {
33
+ static globalConfig = null;
34
+ iframe = null;
35
+ options;
36
+ messageListener = null;
37
+ isReady = false;
38
+ static init(config) {
39
+ BloqueCheckout.globalConfig = config;
40
+ }
41
+ static getGlobalConfig() {
42
+ return BloqueCheckout.globalConfig;
43
+ }
44
+ constructor(options){
45
+ if (!options.checkoutId) throw new Error('[BloqueCheckout] checkoutId is required');
46
+ const publicApiKey = options.publicApiKey || BloqueCheckout.globalConfig?.publicApiKey;
47
+ const mode = options.mode || BloqueCheckout.globalConfig?.mode || 'production';
48
+ const checkoutUrl = options.checkoutUrl || BloqueCheckout.globalConfig?.checkoutUrl || DEFAULT_CHECKOUT_URL;
49
+ if (!publicApiKey) throw new Error('[BloqueCheckout] publicApiKey is required. Either pass it as an option or call BloqueCheckout.init() first.');
50
+ this.options = {
51
+ checkoutId: options.checkoutId,
52
+ publicApiKey,
53
+ mode,
54
+ checkoutUrl,
55
+ appearance: options.appearance,
56
+ onReady: options.onReady,
57
+ onSuccess: options.onSuccess,
58
+ onError: options.onError,
59
+ onPending: options.onPending,
60
+ iframeStyles: options.iframeStyles
61
+ };
62
+ }
63
+ createIframe() {
64
+ if (this.iframe) return this.iframe;
65
+ this.iframe = document.createElement('iframe');
66
+ let iframeUrl = this.options.checkoutUrl;
67
+ if (this.options.appearance) {
68
+ const params = new URLSearchParams();
69
+ if (this.options.appearance.primaryColor) params.set('primaryColor', this.options.appearance.primaryColor);
70
+ if (this.options.appearance.borderRadius) params.set('borderRadius', this.options.appearance.borderRadius);
71
+ if (this.options.appearance.fontFamily) params.set('fontFamily', this.options.appearance.fontFamily);
72
+ const queryString = params.toString();
73
+ if (queryString) iframeUrl = `${iframeUrl}?${queryString}`;
74
+ }
75
+ this.iframe.src = iframeUrl;
76
+ this.iframe.setAttribute('frameborder', '0');
77
+ this.iframe.setAttribute('allowtransparency', 'true');
78
+ this.iframe.setAttribute('allow', 'payment');
79
+ const defaultStyles = {
80
+ width: '100%',
81
+ height: '600px',
82
+ border: 'none',
83
+ borderRadius: '8px'
84
+ };
85
+ const styles = {
86
+ ...defaultStyles,
87
+ ...this.options.iframeStyles
88
+ };
89
+ for (const [key, value] of Object.entries(styles))if (null != value) this.iframe.style[key] = value;
90
+ this.setupMessageListener();
91
+ return this.iframe;
92
+ }
93
+ mount(container) {
94
+ const containerElement = 'string' == typeof container ? document.getElementById(container) || document.querySelector(container) : container;
95
+ if (!containerElement) throw new Error('Container element not found');
96
+ const iframe = this.createIframe();
97
+ containerElement.appendChild(iframe);
98
+ }
99
+ setupMessageListener() {
100
+ this.messageListener = (event)=>{
101
+ const { type, data, error } = event.data || {};
102
+ switch(type){
103
+ case 'checkout-ready':
104
+ this.handleCheckoutReady();
105
+ break;
106
+ case 'payment-result':
107
+ if (data) this.handlePaymentResult(data);
108
+ break;
109
+ case 'payment-error':
110
+ if (error) this.handlePaymentError(error);
111
+ break;
112
+ default:
113
+ break;
114
+ }
115
+ };
116
+ window.addEventListener('message', this.messageListener);
117
+ }
118
+ handleCheckoutReady() {
119
+ this.isReady = true;
120
+ if (this.iframe?.contentWindow) this.iframe.contentWindow.postMessage({
121
+ type: 'checkout-init',
122
+ checkoutId: this.options.checkoutId,
123
+ publicApiKey: this.options.publicApiKey,
124
+ mode: this.options.mode
125
+ }, '*');
126
+ this.options.onReady?.();
127
+ }
128
+ handlePaymentResult(data) {
129
+ if ('approved' === data.status) this.options.onSuccess?.(data);
130
+ else if ('pending' === data.status) this.options.onPending?.(data);
131
+ else if ('rejected' === data.status) this.options.onError?.(data.message || 'Payment was rejected');
132
+ }
133
+ handlePaymentError(error) {
134
+ this.options.onError?.(error);
135
+ }
136
+ isCheckoutReady() {
137
+ return this.isReady;
138
+ }
139
+ getIframe() {
140
+ return this.iframe;
141
+ }
142
+ updateOptions(options) {
143
+ this.options = {
144
+ ...this.options,
145
+ ...options,
146
+ checkoutUrl: options.checkoutUrl || this.options.checkoutUrl,
147
+ mode: options.mode || this.options.mode
148
+ };
149
+ }
150
+ destroy() {
151
+ if (this.iframe) {
152
+ this.iframe.remove();
153
+ this.iframe = null;
154
+ }
155
+ if (this.messageListener) {
156
+ window.removeEventListener('message', this.messageListener);
157
+ this.messageListener = null;
158
+ }
159
+ this.isReady = false;
160
+ }
161
+ }
162
+ function init(config) {
163
+ BloqueCheckout.init(config);
164
+ }
165
+ function createCheckout(options) {
166
+ const { container, ...checkoutOptions } = options;
167
+ const checkout = new BloqueCheckout(checkoutOptions);
168
+ checkout.mount(container);
169
+ return checkout;
170
+ }
171
+ exports.BloqueCheckout = __webpack_exports__.BloqueCheckout;
172
+ exports.createCheckout = __webpack_exports__.createCheckout;
173
+ exports.init = __webpack_exports__.init;
174
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
175
+ "BloqueCheckout",
176
+ "createCheckout",
177
+ "init"
178
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
16
179
  Object.defineProperty(exports, '__esModule', {
17
180
  value: true
18
181
  });
package/dist/index.d.ts CHANGED
@@ -1,5 +1,2 @@
1
- export type { CardPaymentFormData } from './types/card-payment';
2
- export type { CashPaymentFormData } from './types/cash-payment';
3
- export type { CreatePaymentParams, PaymentMethodType, PaymentResponse, } from './types/payment';
4
- export type { PaymentSubmitPayload } from './types/payment-submit';
5
- export type { PSEPaymentFormData } from './types/pse-payment';
1
+ export { BloqueCheckout, createCheckout, init } from './checkout';
2
+ export type { AppearanceConfig, BloqueCheckoutOptions, BloqueInitOptions, CheckoutMessage, CheckoutMessageType, PaymentResult, } from './types';
package/dist/index.js CHANGED
@@ -0,0 +1,141 @@
1
+ const DEFAULT_CHECKOUT_URL = 'http://payments.bloque.app/checkout';
2
+ class BloqueCheckout {
3
+ static globalConfig = null;
4
+ iframe = null;
5
+ options;
6
+ messageListener = null;
7
+ isReady = false;
8
+ static init(config) {
9
+ BloqueCheckout.globalConfig = config;
10
+ }
11
+ static getGlobalConfig() {
12
+ return BloqueCheckout.globalConfig;
13
+ }
14
+ constructor(options){
15
+ if (!options.checkoutId) throw new Error('[BloqueCheckout] checkoutId is required');
16
+ const publicApiKey = options.publicApiKey || BloqueCheckout.globalConfig?.publicApiKey;
17
+ const mode = options.mode || BloqueCheckout.globalConfig?.mode || 'production';
18
+ const checkoutUrl = options.checkoutUrl || BloqueCheckout.globalConfig?.checkoutUrl || DEFAULT_CHECKOUT_URL;
19
+ if (!publicApiKey) throw new Error('[BloqueCheckout] publicApiKey is required. Either pass it as an option or call BloqueCheckout.init() first.');
20
+ this.options = {
21
+ checkoutId: options.checkoutId,
22
+ publicApiKey,
23
+ mode,
24
+ checkoutUrl,
25
+ appearance: options.appearance,
26
+ onReady: options.onReady,
27
+ onSuccess: options.onSuccess,
28
+ onError: options.onError,
29
+ onPending: options.onPending,
30
+ iframeStyles: options.iframeStyles
31
+ };
32
+ }
33
+ createIframe() {
34
+ if (this.iframe) return this.iframe;
35
+ this.iframe = document.createElement('iframe');
36
+ let iframeUrl = this.options.checkoutUrl;
37
+ if (this.options.appearance) {
38
+ const params = new URLSearchParams();
39
+ if (this.options.appearance.primaryColor) params.set('primaryColor', this.options.appearance.primaryColor);
40
+ if (this.options.appearance.borderRadius) params.set('borderRadius', this.options.appearance.borderRadius);
41
+ if (this.options.appearance.fontFamily) params.set('fontFamily', this.options.appearance.fontFamily);
42
+ const queryString = params.toString();
43
+ if (queryString) iframeUrl = `${iframeUrl}?${queryString}`;
44
+ }
45
+ this.iframe.src = iframeUrl;
46
+ this.iframe.setAttribute('frameborder', '0');
47
+ this.iframe.setAttribute('allowtransparency', 'true');
48
+ this.iframe.setAttribute('allow', 'payment');
49
+ const defaultStyles = {
50
+ width: '100%',
51
+ height: '600px',
52
+ border: 'none',
53
+ borderRadius: '8px'
54
+ };
55
+ const styles = {
56
+ ...defaultStyles,
57
+ ...this.options.iframeStyles
58
+ };
59
+ for (const [key, value] of Object.entries(styles))if (null != value) this.iframe.style[key] = value;
60
+ this.setupMessageListener();
61
+ return this.iframe;
62
+ }
63
+ mount(container) {
64
+ const containerElement = 'string' == typeof container ? document.getElementById(container) || document.querySelector(container) : container;
65
+ if (!containerElement) throw new Error('Container element not found');
66
+ const iframe = this.createIframe();
67
+ containerElement.appendChild(iframe);
68
+ }
69
+ setupMessageListener() {
70
+ this.messageListener = (event)=>{
71
+ const { type, data, error } = event.data || {};
72
+ switch(type){
73
+ case 'checkout-ready':
74
+ this.handleCheckoutReady();
75
+ break;
76
+ case 'payment-result':
77
+ if (data) this.handlePaymentResult(data);
78
+ break;
79
+ case 'payment-error':
80
+ if (error) this.handlePaymentError(error);
81
+ break;
82
+ default:
83
+ break;
84
+ }
85
+ };
86
+ window.addEventListener('message', this.messageListener);
87
+ }
88
+ handleCheckoutReady() {
89
+ this.isReady = true;
90
+ if (this.iframe?.contentWindow) this.iframe.contentWindow.postMessage({
91
+ type: 'checkout-init',
92
+ checkoutId: this.options.checkoutId,
93
+ publicApiKey: this.options.publicApiKey,
94
+ mode: this.options.mode
95
+ }, '*');
96
+ this.options.onReady?.();
97
+ }
98
+ handlePaymentResult(data) {
99
+ if ('approved' === data.status) this.options.onSuccess?.(data);
100
+ else if ('pending' === data.status) this.options.onPending?.(data);
101
+ else if ('rejected' === data.status) this.options.onError?.(data.message || 'Payment was rejected');
102
+ }
103
+ handlePaymentError(error) {
104
+ this.options.onError?.(error);
105
+ }
106
+ isCheckoutReady() {
107
+ return this.isReady;
108
+ }
109
+ getIframe() {
110
+ return this.iframe;
111
+ }
112
+ updateOptions(options) {
113
+ this.options = {
114
+ ...this.options,
115
+ ...options,
116
+ checkoutUrl: options.checkoutUrl || this.options.checkoutUrl,
117
+ mode: options.mode || this.options.mode
118
+ };
119
+ }
120
+ destroy() {
121
+ if (this.iframe) {
122
+ this.iframe.remove();
123
+ this.iframe = null;
124
+ }
125
+ if (this.messageListener) {
126
+ window.removeEventListener('message', this.messageListener);
127
+ this.messageListener = null;
128
+ }
129
+ this.isReady = false;
130
+ }
131
+ }
132
+ function init(config) {
133
+ BloqueCheckout.init(config);
134
+ }
135
+ function createCheckout(options) {
136
+ const { container, ...checkoutOptions } = options;
137
+ const checkout = new BloqueCheckout(checkoutOptions);
138
+ checkout.mount(container);
139
+ return checkout;
140
+ }
141
+ export { BloqueCheckout, createCheckout, init };
@@ -0,0 +1,93 @@
1
+ export interface AppearanceConfig {
2
+ /**
3
+ * Primary color for buttons and accents
4
+ * @example '#6366f1'
5
+ */
6
+ primaryColor?: string;
7
+ /**
8
+ * Border radius for form elements
9
+ * @example '12px'
10
+ */
11
+ borderRadius?: string;
12
+ /**
13
+ * Font family for text
14
+ * @example 'Inter, system-ui, sans-serif'
15
+ */
16
+ fontFamily?: string;
17
+ }
18
+ export interface BloqueInitOptions {
19
+ /**
20
+ * Your Bloque public API key
21
+ */
22
+ publicApiKey: string;
23
+ /**
24
+ * Operation mode
25
+ * @default 'production'
26
+ */
27
+ mode?: 'production' | 'sandbox';
28
+ /**
29
+ * The URL of the hosted checkout page
30
+ * @default 'https://payments.bloque.app/checkout'
31
+ */
32
+ checkoutUrl?: string;
33
+ }
34
+ export interface BloqueCheckoutOptions {
35
+ /**
36
+ * The checkout ID returned from your backend after creating a checkout session
37
+ */
38
+ checkoutId: string;
39
+ /**
40
+ * Your Bloque public API key (optional if you called BloqueCheckout.init())
41
+ */
42
+ publicApiKey?: string;
43
+ /**
44
+ * Operation mode (optional if you called BloqueCheckout.init())
45
+ * @default 'production'
46
+ */
47
+ mode?: 'production' | 'sandbox';
48
+ /**
49
+ * The URL of the hosted checkout page
50
+ * @default 'https://payments.bloque.app/checkout'
51
+ */
52
+ checkoutUrl?: string;
53
+ /**
54
+ * Appearance configuration for the checkout
55
+ */
56
+ appearance?: AppearanceConfig;
57
+ /**
58
+ * Callback fired when the checkout iframe is ready to receive the checkout ID
59
+ */
60
+ onReady?: () => void;
61
+ /**
62
+ * Callback fired when a payment is successfully approved
63
+ */
64
+ onSuccess?: (data: PaymentResult) => void;
65
+ /**
66
+ * Callback fired when a payment fails or an error occurs
67
+ */
68
+ onError?: (error: string) => void;
69
+ /**
70
+ * Callback fired when a payment is pending (e.g., waiting for bank confirmation)
71
+ */
72
+ onPending?: (data: PaymentResult) => void;
73
+ /**
74
+ * Custom CSS styles to apply to the iframe
75
+ */
76
+ iframeStyles?: Record<string, string>;
77
+ }
78
+ export interface PaymentResult {
79
+ payment_id: string;
80
+ status: 'approved' | 'pending' | 'rejected';
81
+ message: string;
82
+ amount: number;
83
+ currency: string;
84
+ reference: string;
85
+ created_at: string;
86
+ }
87
+ export type CheckoutMessageType = 'checkout-ready' | 'checkout-init' | 'payment-result' | 'payment-error';
88
+ export interface CheckoutMessage {
89
+ type: CheckoutMessageType;
90
+ checkoutId?: string;
91
+ data?: PaymentResult;
92
+ error?: string;
93
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloque/payments-core",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Core utilities and types for Bloque Payments.",
5
5
  "type": "module",
6
6
  "keywords": [