@seaverse/payment-sdk 0.1.0 → 0.2.0

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.
@@ -4141,29 +4141,25 @@ class PaymentClient {
4141
4141
  */
4142
4142
  /**
4143
4143
  * 环境配置
4144
+ * - development: 开发/测试环境
4145
+ * - production: 生产环境
4144
4146
  */
4145
4147
  const ENV_CONFIG = {
4146
- develop: {
4148
+ development: {
4147
4149
  apiHost: 'https://aiart-openresty.dev.seaart.dev',
4148
- iframeUrl: 'https://aiart-payment-page.dev.seaart.dev',
4149
4150
  },
4150
- release: {
4151
+ production: {
4151
4152
  apiHost: 'https://www.seaart.ai',
4152
- iframeUrl: 'https://pay.seaart.ai',
4153
4153
  },
4154
4154
  };
4155
4155
  /**
4156
- * SDK CDN 地址
4156
+ * Web Component 加载超时时间(毫秒)
4157
4157
  */
4158
- const SDK_CDN_URL = 'https://image.cdn2.seaart.me/seaart-payment-sdk/1.0.2/seaart-pay-sdk-umd.min.js';
4158
+ const COMPONENT_LOAD_TIMEOUT = 15000;
4159
4159
  /**
4160
- * SDK 加载超时时间(毫秒)
4160
+ * Web Component 自定义元素名称
4161
4161
  */
4162
- const SDK_LOAD_TIMEOUT = 15000;
4163
- /**
4164
- * SDK 全局对象名称
4165
- */
4166
- const SDK_GLOBAL_NAME = 'SeaArtPay';
4162
+ const PAYMENT_ELEMENT_NAME = 'seaart-payment';
4167
4163
  /**
4168
4164
  * API 端点
4169
4165
  */
@@ -4175,10 +4171,10 @@ const API_ENDPOINTS = {
4175
4171
  * 默认配置
4176
4172
  */
4177
4173
  const DEFAULT_CHECKOUT_CONFIG = {
4178
- environment: 'develop',
4174
+ environment: 'development',
4179
4175
  debug: false,
4180
4176
  language: 'en',
4181
- sdkTimeout: SDK_LOAD_TIMEOUT,
4177
+ componentTimeout: COMPONENT_LOAD_TIMEOUT,
4182
4178
  };
4183
4179
  /**
4184
4180
  * HTTP 状态码
@@ -4202,22 +4198,25 @@ const BIZ_CODE = {
4202
4198
  };
4203
4199
 
4204
4200
  /**
4205
- * SeaArtPay SDK 加载器
4206
- * 负责从 CDN 动态加载 SeaArt 支付 SDK
4201
+ * SeaArt Payment Web Component 加载器
4202
+ * 负责动态加载 @seaart/payment-component
4207
4203
  */
4208
4204
  /**
4209
- * SDK 加载器类
4205
+ * Web Component 加载器类
4206
+ *
4207
+ * 负责:
4208
+ * - 动态导入 @seaart/payment-component
4209
+ * - 等待自定义元素注册完成
4210
+ * - 管理加载状态
4210
4211
  */
4211
4212
  class SeaArtPayLoader {
4212
4213
  constructor(config = {}) {
4213
4214
  this.status = 'idle';
4214
- this.sdk = null;
4215
4215
  this.loadPromise = null;
4216
4216
  this.config = {
4217
- cdnUrl: config.cdnUrl ?? SDK_CDN_URL,
4218
- timeout: config.timeout ?? SDK_LOAD_TIMEOUT,
4217
+ timeout: config.timeout ?? COMPONENT_LOAD_TIMEOUT,
4219
4218
  debug: config.debug ?? false,
4220
- environment: config.environment ?? 'develop',
4219
+ environment: config.environment ?? 'development',
4221
4220
  };
4222
4221
  }
4223
4222
  /**
@@ -4227,38 +4226,49 @@ class SeaArtPayLoader {
4227
4226
  return this.status;
4228
4227
  }
4229
4228
  /**
4230
- * 检查 SDK 是否已加载
4229
+ * 检查组件是否已加载
4231
4230
  */
4232
4231
  isLoaded() {
4233
- return this.status === 'loaded' && this.sdk !== null;
4232
+ return this.status === 'loaded';
4234
4233
  }
4235
4234
  /**
4236
- * 获取 SDK 实例
4235
+ * 检查自定义元素是否已注册
4237
4236
  */
4238
- getSDK() {
4239
- return this.sdk;
4237
+ isElementDefined() {
4238
+ if (typeof customElements === 'undefined') {
4239
+ return false;
4240
+ }
4241
+ return customElements.get(PAYMENT_ELEMENT_NAME) !== undefined;
4240
4242
  }
4241
4243
  /**
4242
- * 加载 SDK
4244
+ * 加载 Web Component
4243
4245
  */
4244
4246
  async load() {
4245
4247
  // 如果已加载,直接返回
4246
- if (this.sdk) {
4247
- return this.sdk;
4248
+ if (this.status === 'loaded') {
4249
+ return;
4248
4250
  }
4249
4251
  // 如果正在加载,返回现有的 Promise
4250
4252
  if (this.loadPromise) {
4251
4253
  return this.loadPromise;
4252
4254
  }
4255
+ // 检查是否在浏览器环境
4256
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
4257
+ throw new Error('非浏览器环境,无法加载 Web Component');
4258
+ }
4259
+ // 如果自定义元素已注册,直接标记为已加载
4260
+ if (this.isElementDefined()) {
4261
+ this.status = 'loaded';
4262
+ this.log('Web Component 已注册,跳过加载');
4263
+ return;
4264
+ }
4253
4265
  this.status = 'loading';
4254
- this.log('开始加载 SeaArtPay SDK...');
4255
- this.loadPromise = this.loadSDKFromSources();
4266
+ this.log('开始加载 SeaArt Payment Web Component...');
4267
+ this.loadPromise = this.doLoad();
4256
4268
  try {
4257
- this.sdk = await this.loadPromise;
4269
+ await this.loadPromise;
4258
4270
  this.status = 'loaded';
4259
- this.initializeSDK();
4260
- this.log('SDK 加载成功');
4261
- return this.sdk;
4271
+ this.log('Web Component 加载成功');
4262
4272
  }
4263
4273
  catch (error) {
4264
4274
  this.status = 'error';
@@ -4267,117 +4277,67 @@ class SeaArtPayLoader {
4267
4277
  }
4268
4278
  }
4269
4279
  /**
4270
- * 从多个源尝试加载 SDK
4271
- */
4272
- async loadSDKFromSources() {
4273
- const sources = [
4274
- { name: 'CDN', url: this.config.cdnUrl },
4275
- ];
4276
- for (const source of sources) {
4277
- this.log(`尝试从 ${source.name} 加载: ${source.url}`);
4278
- try {
4279
- await this.loadScript(source.url);
4280
- const sdk = this.extractSDKInstance();
4281
- if (sdk) {
4282
- this.log(`成功从 ${source.name} 加载 SDK`);
4283
- return sdk;
4284
- }
4285
- this.log(`${source.name} 加载的脚本无效`);
4286
- }
4287
- catch (error) {
4288
- this.log(`从 ${source.name} 加载失败: ${error}`);
4289
- }
4290
- }
4291
- throw new Error('所有 SDK 源加载失败');
4292
- }
4293
- /**
4294
- * 动态加载脚本
4280
+ * 执行加载逻辑
4295
4281
  */
4296
- loadScript(url) {
4297
- return new Promise((resolve, reject) => {
4298
- // 检查是否在浏览器环境
4299
- if (typeof document === 'undefined') {
4300
- reject(new Error('非浏览器环境,无法加载脚本'));
4301
- return;
4302
- }
4303
- // 检查是否已加载
4304
- const existingScript = document.querySelector(`script[src="${url}"]`);
4305
- if (existingScript) {
4306
- resolve();
4307
- return;
4308
- }
4309
- const script = document.createElement('script');
4310
- script.src = url;
4311
- script.async = true;
4312
- script.setAttribute('data-sdk', 'seaart-payment');
4313
- const timer = setTimeout(() => {
4314
- cleanup();
4315
- reject(new Error(`脚本加载超时: ${url}`));
4282
+ async doLoad() {
4283
+ let timeoutId = null;
4284
+ // 创建超时 Promise
4285
+ const timeoutPromise = new Promise((_, reject) => {
4286
+ timeoutId = setTimeout(() => {
4287
+ reject(new Error(`Web Component 加载超时(${this.config.timeout}ms)`));
4316
4288
  }, this.config.timeout);
4317
- const cleanup = () => {
4318
- clearTimeout(timer);
4319
- script.onload = null;
4320
- script.onerror = null;
4321
- };
4322
- script.onload = () => {
4323
- cleanup();
4324
- resolve();
4325
- };
4326
- script.onerror = () => {
4327
- cleanup();
4328
- document.head.removeChild(script);
4329
- reject(new Error(`脚本加载失败: ${url}`));
4330
- };
4331
- document.head.appendChild(script);
4332
4289
  });
4290
+ // 创建加载 Promise
4291
+ const loadPromise = this.loadComponent();
4292
+ try {
4293
+ // 竞争:加载完成 或 超时
4294
+ await Promise.race([loadPromise, timeoutPromise]);
4295
+ }
4296
+ finally {
4297
+ // 清理超时定时器
4298
+ if (timeoutId) {
4299
+ clearTimeout(timeoutId);
4300
+ }
4301
+ }
4333
4302
  }
4334
4303
  /**
4335
- * 提取 SDK 实例
4336
- * 处理 UMD 包装对象的情况
4304
+ * 加载组件
4337
4305
  */
4338
- extractSDKInstance() {
4339
- if (typeof window === 'undefined') {
4340
- return null;
4341
- }
4342
- const globalObj = window.SeaArtPay;
4343
- if (!globalObj) {
4344
- return null;
4345
- }
4346
- // 检查是否是实际的 SDK 实例(有 show 方法)
4347
- if (this.isValidSDK(globalObj)) {
4348
- return globalObj;
4306
+ async loadComponent() {
4307
+ try {
4308
+ // 动态导入 @seaart/payment-component
4309
+ // 注意:消费方需要在项目中安装此依赖
4310
+ await import('@seaart/payment-component');
4311
+ this.log('模块导入成功');
4312
+ // 等待自定义元素注册完成
4313
+ await this.waitForElementDefined();
4314
+ this.log('自定义元素注册完成');
4349
4315
  }
4350
- // 检查是否是 UMD 包装对象
4351
- const wrapped = globalObj;
4352
- if (wrapped.SeaArtPay && this.isValidSDK(wrapped.SeaArtPay)) {
4353
- // 修复全局引用
4354
- window.SeaArtPay = wrapped.SeaArtPay;
4355
- return wrapped.SeaArtPay;
4316
+ catch (error) {
4317
+ // 如果动态导入失败,可能是消费方没有安装依赖
4318
+ const errorMessage = error instanceof Error ? error.message : String(error);
4319
+ throw new Error(`无法加载 @seaart/payment-component: ${errorMessage}\n` +
4320
+ '请确保已安装依赖: npm install @seaart/payment-component');
4356
4321
  }
4357
- return null;
4358
4322
  }
4359
4323
  /**
4360
- * 验证 SDK 实例是否有效
4324
+ * 等待自定义元素注册
4361
4325
  */
4362
- isValidSDK(sdk) {
4363
- return (typeof sdk === 'object' &&
4364
- sdk !== null &&
4365
- typeof sdk.show === 'function');
4326
+ async waitForElementDefined() {
4327
+ if (typeof customElements === 'undefined') {
4328
+ throw new Error('浏览器不支持 Custom Elements');
4329
+ }
4330
+ // 使用 customElements.whenDefined 等待元素注册
4331
+ await customElements.whenDefined(PAYMENT_ELEMENT_NAME);
4366
4332
  }
4367
4333
  /**
4368
- * 初始化 SDK
4334
+ * 创建支付元素
4369
4335
  */
4370
- initializeSDK() {
4371
- if (!this.sdk)
4372
- return;
4373
- const envConfig = ENV_CONFIG[this.config.environment];
4374
- if (typeof this.sdk.init === 'function') {
4375
- this.sdk.init({
4376
- debug: this.config.debug,
4377
- iframeUrl: envConfig.iframeUrl,
4378
- });
4379
- this.log(`SDK 已初始化,环境: ${this.config.environment}`);
4336
+ createElement() {
4337
+ if (!this.isLoaded() && !this.isElementDefined()) {
4338
+ throw new Error('Web Component 尚未加载,请先调用 load() 方法');
4380
4339
  }
4340
+ return document.createElement(PAYMENT_ELEMENT_NAME);
4381
4341
  }
4382
4342
  /**
4383
4343
  * 日志输出
@@ -4392,7 +4352,6 @@ class SeaArtPayLoader {
4392
4352
  */
4393
4353
  reset() {
4394
4354
  this.status = 'idle';
4395
- this.sdk = null;
4396
4355
  this.loadPromise = null;
4397
4356
  }
4398
4357
  }
@@ -4576,12 +4535,14 @@ class CheckoutAPI {
4576
4535
  /**
4577
4536
  * PaymentCheckoutClient - 支付弹窗客户端
4578
4537
  * 提供支付弹窗的初始化、结账和订阅功能
4538
+ *
4539
+ * 使用 @seaart/payment-component Web Component 实现支付弹窗
4579
4540
  */
4580
4541
  /**
4581
4542
  * 支付弹窗客户端类
4582
4543
  *
4583
4544
  * 提供一次性购买和订阅购买功能,自动处理:
4584
- * - SeaArtPay SDK 的动态加载
4545
+ * - @seaart/payment-component Web Component 的动态加载
4585
4546
  * - 创建结账订单
4586
4547
  * - 显示支付弹窗
4587
4548
  * - 支付结果回调
@@ -4592,7 +4553,7 @@ class CheckoutAPI {
4592
4553
  * const checkoutClient = new PaymentCheckoutClient({
4593
4554
  * apiHost: 'https://payment.sg.seaverse.dev',
4594
4555
  * authToken: 'your-jwt-token',
4595
- * environment: 'develop',
4556
+ * environment: 'development',
4596
4557
  * debug: true,
4597
4558
  * });
4598
4559
  *
@@ -4611,6 +4572,8 @@ class PaymentCheckoutClient {
4611
4572
  constructor(config) {
4612
4573
  this.status = 'idle';
4613
4574
  this.initPromise = null;
4575
+ /** 当前显示的支付元素 */
4576
+ this.currentPaymentElement = null;
4614
4577
  // 验证必须提供 authToken 或 getAuthToken 其中之一
4615
4578
  if (!config.authToken && !config.getAuthToken) {
4616
4579
  throw new Error('必须提供 authToken 或 getAuthToken 其中之一');
@@ -4620,10 +4583,9 @@ class PaymentCheckoutClient {
4620
4583
  environment: config.environment ?? DEFAULT_CHECKOUT_CONFIG.environment,
4621
4584
  debug: config.debug ?? DEFAULT_CHECKOUT_CONFIG.debug,
4622
4585
  };
4623
- // 创建 SDK 加载器
4586
+ // 创建 Web Component 加载器
4624
4587
  this.loader = new SeaArtPayLoader({
4625
- cdnUrl: config.sdkCdnUrl,
4626
- timeout: config.sdkTimeout ?? DEFAULT_CHECKOUT_CONFIG.sdkTimeout,
4588
+ timeout: config.componentTimeout ?? DEFAULT_CHECKOUT_CONFIG.componentTimeout,
4627
4589
  debug: this.config.debug,
4628
4590
  environment: this.config.environment,
4629
4591
  });
@@ -4652,7 +4614,7 @@ class PaymentCheckoutClient {
4652
4614
  }
4653
4615
  /**
4654
4616
  * 初始化客户端
4655
- * 加载 SeaArtPay SDK
4617
+ * 加载 @seaart/payment-component Web Component
4656
4618
  */
4657
4619
  async init() {
4658
4620
  if (this.status === 'ready') {
@@ -4680,7 +4642,7 @@ class PaymentCheckoutClient {
4680
4642
  await this.loader.load();
4681
4643
  }
4682
4644
  catch (error) {
4683
- throw this.createError('SDK_LOAD_FAILED', 'SeaArt 支付 SDK 加载失败', error);
4645
+ throw this.createError('COMPONENT_LOAD_FAILED', 'SeaArt Payment Web Component 加载失败', error);
4684
4646
  }
4685
4647
  }
4686
4648
  /**
@@ -4766,7 +4728,7 @@ class PaymentCheckoutClient {
4766
4728
  */
4767
4729
  showPayment(options) {
4768
4730
  if (!this.isReady()) {
4769
- throw this.createError('SDK_NOT_READY', '支付 SDK 未初始化');
4731
+ throw this.createError('COMPONENT_NOT_READY', '支付组件未初始化');
4770
4732
  }
4771
4733
  this.showPaymentModal(options);
4772
4734
  }
@@ -4774,9 +4736,9 @@ class PaymentCheckoutClient {
4774
4736
  * 关闭支付弹窗
4775
4737
  */
4776
4738
  close() {
4777
- const sdk = this.getSDK();
4778
- if (sdk && typeof sdk.close === 'function') {
4779
- sdk.close();
4739
+ if (this.currentPaymentElement) {
4740
+ this.currentPaymentElement.remove();
4741
+ this.currentPaymentElement = null;
4780
4742
  this.log('支付弹窗已关闭');
4781
4743
  }
4782
4744
  }
@@ -4800,45 +4762,77 @@ class PaymentCheckoutClient {
4800
4762
  }
4801
4763
  await this.init();
4802
4764
  }
4803
- /**
4804
- * 获取 SDK 实例
4805
- */
4806
- getSDK() {
4807
- return this.loader.getSDK();
4808
- }
4809
4765
  /**
4810
4766
  * 显示支付弹窗
4767
+ * 使用 <seaart-payment> Web Component
4811
4768
  */
4812
4769
  showPaymentModal(options) {
4813
- const sdk = this.getSDK();
4814
- if (!sdk) {
4815
- throw this.createError('SDK_NOT_READY', '支付 SDK 未加载');
4816
- }
4770
+ // 如果已有支付弹窗,先关闭
4771
+ this.close();
4817
4772
  this.log('打开支付弹窗:', options.transactionId);
4818
- sdk.show({
4819
- transactionId: options.transactionId,
4820
- language: options.language ?? DEFAULT_CHECKOUT_CONFIG.language,
4821
- userName: options.userName,
4822
- productName: options.productName,
4823
- from: options.from,
4824
- onSuccess: (result) => {
4825
- this.log('支付成功:', result);
4826
- options.onSuccess?.({
4827
- transactionId: options.transactionId,
4828
- status: 'success',
4829
- data: result,
4830
- });
4831
- },
4832
- onUnsuccess: (result) => {
4833
- this.log('支付未成功:', result);
4834
- const unsuccessResult = result;
4835
- options.onUnsuccess?.({
4836
- reason: unsuccessResult.reason ?? 'error',
4837
- message: unsuccessResult.message ?? '支付未完成',
4838
- transactionId: options.transactionId,
4839
- });
4840
- },
4773
+ // 创建 Web Component 元素
4774
+ const payment = document.createElement(PAYMENT_ELEMENT_NAME);
4775
+ // 设置必需属性
4776
+ payment.setAttribute('transaction-id', options.transactionId);
4777
+ payment.setAttribute('environment', this.config.environment);
4778
+ payment.setAttribute('language', options.language ?? DEFAULT_CHECKOUT_CONFIG.language);
4779
+ // 设置可选属性
4780
+ if (options.userName) {
4781
+ payment.setAttribute('user-name', options.userName);
4782
+ }
4783
+ if (options.productName) {
4784
+ payment.setAttribute('product-name', options.productName);
4785
+ }
4786
+ if (options.from) {
4787
+ payment.setAttribute('from', options.from);
4788
+ }
4789
+ // 监听成功事件
4790
+ payment.addEventListener('success', ((event) => {
4791
+ this.log('支付成功:', event.detail);
4792
+ const result = {
4793
+ transactionId: options.transactionId,
4794
+ status: 'success',
4795
+ data: event.detail,
4796
+ };
4797
+ options.onSuccess?.(result);
4798
+ this.removePaymentElement(payment);
4799
+ }));
4800
+ // 监听未成功事件
4801
+ payment.addEventListener('unsuccess', ((event) => {
4802
+ this.log('支付未成功:', event.detail);
4803
+ const unsuccessResult = {
4804
+ reason: event.detail.reason ?? 'error',
4805
+ message: event.detail.message ?? '支付未完成',
4806
+ transactionId: options.transactionId,
4807
+ };
4808
+ options.onUnsuccess?.(unsuccessResult);
4809
+ this.removePaymentElement(payment);
4810
+ }));
4811
+ // 监听关闭事件
4812
+ payment.addEventListener('close', () => {
4813
+ this.log('用户关闭支付弹窗');
4814
+ // 触发取消回调
4815
+ options.onUnsuccess?.({
4816
+ reason: 'cancel',
4817
+ message: '用户关闭支付',
4818
+ transactionId: options.transactionId,
4819
+ });
4820
+ this.removePaymentElement(payment);
4841
4821
  });
4822
+ // 添加到 DOM
4823
+ document.body.appendChild(payment);
4824
+ this.currentPaymentElement = payment;
4825
+ }
4826
+ /**
4827
+ * 移除支付元素
4828
+ */
4829
+ removePaymentElement(element) {
4830
+ if (element.parentNode) {
4831
+ element.remove();
4832
+ }
4833
+ if (this.currentPaymentElement === element) {
4834
+ this.currentPaymentElement = null;
4835
+ }
4842
4836
  }
4843
4837
  /**
4844
4838
  * 创建支付错误
@@ -5025,6 +5019,7 @@ function getSDKLocale(locale) {
5025
5019
  *
5026
5020
  * ## 2. Payment Checkout (PaymentCheckoutClient)
5027
5021
  * Show payment modal for one-time purchases and subscriptions.
5022
+ * Uses @seaart/payment-component Web Component internally.
5028
5023
  *
5029
5024
  * ```typescript
5030
5025
  * import { PaymentCheckoutClient } from '@seaverse/payment-sdk';
@@ -5032,7 +5027,7 @@ function getSDKLocale(locale) {
5032
5027
  * const checkoutClient = new PaymentCheckoutClient({
5033
5028
  * apiHost: 'https://payment.sg.seaverse.dev',
5034
5029
  * authToken: 'your-jwt-token',
5035
- * environment: 'develop',
5030
+ * environment: 'development', // or 'production'
5036
5031
  * });
5037
5032
  *
5038
5033
  * await checkoutClient.init();
@@ -5052,7 +5047,7 @@ function getSDKLocale(locale) {
5052
5047
  /**
5053
5048
  * SDK version
5054
5049
  */
5055
- const VERSION = '0.1.0';
5050
+ const VERSION = '0.2.0';
5056
5051
 
5057
- export { API_ENDPOINTS, BIZ_CODE, CheckoutAPI, DEFAULT_CHECKOUT_CONFIG, ENV_CONFIG, HTTP_STATUS, PaymentAPIError, PaymentCheckoutClient, PaymentClient, SDK_CDN_URL, SDK_GLOBAL_NAME, SDK_LOAD_TIMEOUT, SeaArtPayLoader, VERSION, centsToDollars, createCheckoutPaymentError, delay, dollarsToCents, formatPrice, generateOrderReference, getCurrentUrl, getGlobalLoader, getSDKLocale, isBrowser, isCheckoutPaymentError, resetGlobalLoader, safeJsonParse, withTimeout };
5052
+ export { API_ENDPOINTS, BIZ_CODE, COMPONENT_LOAD_TIMEOUT, CheckoutAPI, DEFAULT_CHECKOUT_CONFIG, ENV_CONFIG, HTTP_STATUS, PAYMENT_ELEMENT_NAME, PaymentAPIError, PaymentCheckoutClient, PaymentClient, SeaArtPayLoader, VERSION, centsToDollars, createCheckoutPaymentError, delay, dollarsToCents, formatPrice, generateOrderReference, getCurrentUrl, getGlobalLoader, getSDKLocale, isBrowser, isCheckoutPaymentError, resetGlobalLoader, safeJsonParse, withTimeout };
5058
5053
  //# sourceMappingURL=index.browser.js.map