You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于ReactJS与Node后端的Conekta支付集成问题咨询

React前端+Node后端集成Conekta支付服务配置方案

核心问题定位

你的代码存在两个关键问题:

  1. Conekta脚本异步加载,但调用工具方法时未确保脚本已加载完成,会导致window.Conekta未定义的报错
  2. 缺少脚本加载失败的处理逻辑,也没有对Conekta初始化状态做校验

修正后的前端实现

1. 优化App.js的脚本加载逻辑

添加脚本加载完成回调,确保Conekta初始化时机正确,同时加入加载状态管理:

import { useState, useEffect } from 'react';
import conektaHelper from './conektaHelper';

function App() {
  const [conektaLoaded, setConektaLoaded] = useState(false);

  useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://cdn.conekta.io/js/latest/conekta.js";
    script.async = true;

    // 脚本加载成功后初始化Conekta
    script.onload = () => {
      try {
        conektaHelper.initConekta();
        setConektaLoaded(true);
      } catch (err) {
        console.error("Conekta初始化失败:", err);
      }
    };

    // 脚本加载失败提示
    script.onerror = () => {
      console.error("Conekta服务加载失败,请检查网络或CDN地址");
    };

    document.body.appendChild(script);

    // 组件卸载时清理脚本
    return () => {
      document.body.removeChild(script);
    };
  }, []);

  // 令牌生成示例
  const handleGenerateToken = () => {
    if (!conektaLoaded) {
      alert("支付服务尚未就绪,请稍后重试");
      return;
    }

    conektaHelper.tokenize(
      "4242424242424242", // 测试卡号
      "Juan Pérez",
      "12",
      "28",
      "123",
      (token) => {
        console.log("令牌生成成功:", token.id);
        // 将令牌发送到后端创建订单
        fetch('/api/create-payment', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ tokenId: token.id, amount: 1500 }) // 金额单位为分(15.00 MXN)
        }).then(res => res.json())
          .then(data => console.log("订单创建结果:", data))
          .catch(err => console.error("订单请求失败:", err));
      },
      (error) => {
        console.error("令牌生成失败:", error.message);
        alert(`支付验证失败: ${error.message}`);
      }
    );
  };

  return (
    <div className="App">
      {conektaLoaded ? (
        <button onClick={handleGenerateToken}>测试支付</button>
      ) : (
        <p>加载支付服务中...</p>
      )}
    </div>
  );
}

export default App;

2. 增强Conekta工具类的安全性

添加加载状态校验,处理卡号空格等细节,避免无效调用:

import config from './config'

const conektaHelper = {
  // 检查Conekta是否已加载
  isConektaReady: () => {
    return typeof window !== 'undefined' && !!window.Conekta;
  },

  initConekta: () => {
    if (!conektaHelper.isConektaReady()) {
      throw new Error("Conekta脚本未加载完成");
    }
    // 设置公钥(对应测试/生产环境)
    window.Conekta.setPublicKey(config.conekta.staging.publicKey);
    // 设置语言(可选,默认西班牙语)
    window.Conekta.setLocale('es');
  },

  getCardBrand: (cardNumber) => {
    if (!conektaHelper.isConektaReady()) throw new Error("Conekta未就绪");
    // 移除卡号中的空格再检测品牌
    return window.Conekta.card.getBrand(cardNumber.replace(/\s/g, ''));
  },

  validateCardNumber: (cardNumber) => {
    if (!conektaHelper.isConektaReady()) throw new Error("Conekta未就绪");
    return window.Conekta.card.validateNumber(cardNumber.replace(/\s/g, ''));
  },

  validateCvc: (cvc) => {
    if (!conektaHelper.isConektaReady()) throw new Error("Conekta未就绪");
    return window.Conekta.card.validateCVC(cvc.trim());
  },

  validateExpirationDate: (expiryMonth, expiryYear) => {
    if (!conektaHelper.isConektaReady()) throw new Error("Conekta未就绪");
    // 统一转为4位年份
    const fullYear = expiryYear.length === 2 ? `20${expiryYear}` : expiryYear;
    return window.Conekta.card.validateExpirationDate(expiryMonth, fullYear);
  },

  tokenize: (cardNumber, cardHolder, expiryMonth, expiryYear, cvc, successCallback, errorCallback) => {
    if (!conektaHelper.isConektaReady()) {
      errorCallback(new Error("支付服务未就绪"));
      return;
    }

    const tokenParams = {
      card: {
        number: cardNumber.replace(/\s/g, ''),
        name: cardHolder.trim(),
        exp_year: expiryYear.length === 2 ? `20${expiryYear}` : expiryYear,
        exp_month: expiryMonth,
        cvc: cvc.trim()
      }
    };

    window.Conekta.Token.create(tokenParams, successCallback, errorCallback);
  }
}

export default conektaHelper

Node后端实现(创建支付订单)

使用Conekta官方SDK处理后端支付逻辑,绝对不要在前端暴露私钥:

  1. 安装依赖:
npm install conekta
  1. 创建支付路由(示例):
const express = require('express');
const router = express.Router();
const Conekta = require('conekta');

// 初始化Conekta(使用私钥,测试/生产环境需切换)
Conekta.api_key = '你的Conekta测试私钥';
Conekta.locale = 'es';
Conekta.api_version = '2.0.0';

router.post('/api/create-payment', async (req, res) => {
  const { tokenId, amount } = req.body;

  try {
    // 创建订单
    const order = await Conekta.Order.create({
      currency: 'MXN',
      amount: amount,
      line_items: [{
        name: '示例商品',
        quantity: 1,
        unit_price: amount
      }],
      charges: [{
        payment_method: {
          type: 'card',
          token_id: tokenId
        }
      }]
    });

    res.status(200).json({ success: true, orderId: order.id, status: order.status });
  } catch (error) {
    res.status(400).json({ success: false, message: error.message });
  }
});

module.exports = router;

关键注意事项

  • 公钥和私钥必须对应环境:测试环境用测试密钥,生产环境切换为生产密钥
  • 所有敏感操作(创建订单、退款、查询订单)必须在后端完成,前端仅负责卡片验证和令牌生成
  • 测试时使用Conekta提供的测试卡号(如4242 4242 4242 4242),过期年月需大于当前日期,CVC任意3位
  • 处理用户输入时,要做格式校验(如卡号自动加空格、年月格式限制),提升用户体验

内容的提问来源于stack exchange,提问作者Rogelio Vazquez Mejia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 12:45:47