ondrakoupil/csob-eapi-paygate

用于轻松集成ČSOB支付网关的PHP客户端库

v1.9.5 2023-01-13 12:59 UTC

README

Build Status Number of downloads Current version Licence

使用本库可以方便地将ČSOB支付网关集成到您的电商店铺或其他PHP应用中,无需直接操作API、调用方法、验证签名等。

英文README在这里.

关于支付网关API、密钥生成和支付处理步骤的详细信息,请参阅https://github.com/csob/platebnibrana。测试卡信息在此处wiki

注意注意!很多人经常在这里遇到问题,我最好在这里提出来。 这里使用的是您的私钥和银行的公钥 不是反过来。不是您的公钥。

更新

ČSOB API 1.9

2022年夏天,银行发布了API版本1.9,除了添加了一些新功能外,还进行了一些更改。因此,如果您在应用程序中使用此库并使用最新版本的地址 GatewayUrl::PRODUCTION_LATEST,请注意,将库更新到1.9版本将导致API更新到1.9,因此某些功能可能会有所不同。

更新

  • One click支付的函数已更改。现在必须调用 paymentOneClickInit() 然后调用 paymentOneClickProcess()
  • 对于 Payment 对象的 paymentInit()paymentOneClickInit() 方法,现在可以传递关于客户和整个交易的大量数据。这些数据由银行传递给发卡行,发卡行根据其内部算法决定是否需要使用3D验证。通过传递这些辅助数据,据说可以增加客户的安全性,因为交易更有可能被批准为安全交易,因为它足够符合其先前的行为和在线支付习惯。所有这些新数据都可以通过 $payment->setCustomer($customer)$payment->setOrder($order) 以及 Metadata 命名空间中的类来设置。

默认地址是当前版本的测试支付网关(现在是1.9 - GatewayUrl::TEST_1_9)。

建议使用包含各个版本API URL的 GatewayUrl 类常量。

$config->url = GatewayUrl::TEST_1_8;
$config->url = GatewayUrl::PRODUCTION_1_9;
$config->url = GatewayUrl::PRODUCTION_LATEST;

安装

最简单的方法是使用Composer安装

composer require ondrakoupil/csob-eapi-paygate

如果您不使用Composer,只需将 dist/csob-client.php 文件复制到某个地方并包含它即可 - 它包含所有必要的类。您可以直接从GitHub 下载此文件,在导出的包中不包含。

使用

除了这个库,您还需要

  • 商户ID - 可以在keygen 页面上生成匿名ID,或者使用银行分配的ID
  • 签名和验证签名的密钥 - 同样可以在 keygen 中获取。在实施过程中,您只需使用自己的私钥。公钥通过keygen发送给银行,然后您可以将其忽略。
  • 银行公钥 - 可以从 ČSOB的GitHub 下载。请注意,测试和正式网关的公钥不同。

库由以下类组成

  • Client - 我们将与之交互的主要类
  • Config - 通信参数、密钥、Merchant ID等的设置,以及各种默认值
  • Payment - 代表一笔支付
  • Crypto - 负责签名和验证签名
  • 扩展 - 表示扩展的类。可以直接使用 Extension 类,也可以使用各个专门的派生类。

所有类都在命名空间 OndraKoupil\Csob 中,因此需要在文件开头使用 use,或者总是使用包括命名空间在内的完整类名。这里提供的示例假设您已经使用了 use

设置

首先需要创建一个 Config 对象,并设置其中所需值。然后,将此对象传递给 Client 对象,并调用其方法,这些方法对应于 API 通常提供的方法。

$config = new Config(
	"My Merchant ID",
	"path/to/my/private/key/file.key",
	"path/to/bank/public/key.pub",
	"My shop name",

	// Adresa, kam se mají zákazníci vracet poté, co zaplatí
	"https://www.my-eshop.cz/return-path.php",

	// URL adresa API - výchozí je adresa testovacího (integračního) prostředí,
	// až budete připraveni přepnout se na ostré rozhraní, sem zadáte
	// adresu ostrého API. Nezapomeňte také na ostrý veřejný klíč banky.
	GatewayUrl::TEST_LATEST
);

$client = new Client($config);

注意 - 这里使用的是您的私钥和银行的公钥。 也要记住,测试和正式 API 有不同的公钥。

Config 允许设置其他参数和一些默认值,请参阅 文档

连接测试

为了验证连接是否正常工作并且请求是否正确签名,可以使用 testGetConnection() 和 testPostConnection() 方法,这些方法调用 API 的 echo 方法。

try {
	$client->testGetConnection();
	$client->testPostConnection();

} catch (Exception $e) {
	echo "Something went wrong: " . $e->getMessage();
}

创建新的支付(payment/init)

要创建新的支付,需要创建一个 Payment 对象,设置所需值,然后将其传递给 paymentInit()。如果一切正常,API 将为支付分配 PayID。这需要保存起来,以后调用其他方法时会用到。

使用 $payment->addCartItem() 添加商品到订单。在当前版本中,支付必须有一个或两个商品,在未来的版本中,这一限制将有所改变。

注意,所有字符串都应该是 UTF-8 编码。如果您使用其他编码,那么在存在变音符号的地方(特别是购物车中的商品名称),需要使用 iconv 函数进行转换。

$payment = new Payment("1234");
$payment->addCartItem("Zakoupená věcička", 1, 10000);

$response = $client->paymentInit($payment);

$payId = $payment->getPayId();
$payId = $response["payId"];

这是必需的最小值 - 在 $payment 对象中可以设置更多。注意,价格以基本货币单位(分或美分)的百分之一表示,即 10000 表示 100 Kč。

调用 paymentInit() 时,将给 $payment 对象设置其 PayID,可以通过 getter 读取,或者从返回的数组中获取。

可选地,从 API 1.9 开始,可以将包含用户元数据的 Payment 对象传递给 API,这可以简化发卡行对交易的审批。

$customer = new Customer();
$customer->name = 'John Rambo';
$customer->email = 'john@rambo.cz';
$payment->setCustomer($customer);

支付(payment/process)

在成功创建支付后,需要将客户浏览器重定向到由 getPaymentProcessUrl() 生成的支付网关地址。这里还有一个方法 redirectToGateway(),可以直接执行重定向。

$url = $client->getPaymentProcessUrl($payment);
redirectBrowserTo($url);  // fiktivní funkce pro přesměrování

// NEBO

$client->redirectToGateway($payment);
terminateApp();  // fiktivní funkce pro ukončení skriptu

可以作为参数使用前面调用中使用的 $payment 对象,或者作为普通字符串的 PayID。

客户返回

客户在支付网关输入所需信息并验证和批准后,网关将客户返回到 Return URL,该 URL 您已在 Config 或 Payment 对象中设置。在这个 URL 上,您应该使用 paymentStatus() 验证支付状态,或者简单地使用 receiveReturningCustomer() 方法处理传入的数据,该方法将检查传入数据的签名并从中提取有用值。

$response = $client->receiveReturningCustomer();

if ($response["paymentStatus"] == 7) {
	// nebo také 4, záleží na nastavení closePayment
	echo "Platba proběhla, děkujeme za nákup.";

} else {
	echo "Něco se pokazilo, sakra...";
}

支付状态的详细信息请参阅 支付网关 wiki

验证支付状态(payment/status)

随时可以简单地了解支付当前状态

$status = $client->paymentStatus($payId);

如果需要比状态编号更多的详细信息,将第二个参数 $returnStatusOnly 设置为 false,方法将返回包含不同详细信息的数组。

确认、取消、退款

paymentReverse() 方法将取消尚未处理的支付,paymentClose() 确认支付,paymentRefund() 将已完成的支付退还给付款人。

请注意,支付必须处于正确的状态,否则将引发错误并抛出异常。如果将第二个参数 $ignoreWrongPaymentStatusError 设置为 true,则将静默忽略此特定错误,并且该方法仅返回 null。所有其他错误继续抛出异常。

$client->paymentReverse($payId);
$client->paymentClose($payId);
$client->paymentRefund($payId);

从API 1.5开始,支付网关允许通过 paymentRefund() 方法仅部分退款,或者通过 paymentClose() 方法确认小于最初授权金额的交易。这些方法的第三个参数可以设置要退回的金额,单位为基本货币的 setin(请注意!)

// Potvrdit transakci jen na 100 Kč
$client->paymentClose($payId, false, 10000);

// Vrátit 100 Kč
$client->paymentRefund($payId, false, 10000);

paymentRefund() 在测试环境中有时会返回HTTP状态500,从而导致抛出异常。根据 此问题,这是支付网关测试环境中的bug,目前尚未解决。

客户信息(customer/info)

customerInfo() 方法检查具有指定ID(例如电子邮件)的客户是否曾经使用信用卡支付,如果是,则可以采取某些措施(例如打印个性化消息)。

$hasCards = $client->customerInfo($someCustomerId);
if ($hasCards) {
	echo "Chcete zase zaplatit kartou?";
} else {
	echo "Nabízíme tyto možnosti platby: ...";
}

API 1.8将此方法重命名为 echo/customer,库将自动选择合适的端点,但在PHP中始终调用 customerInfo()

支付/结账

这是一个尚未公开的功能。它允许在您的网站上显示一个简洁的iframe,并在其中完成整个支付和订单处理,而不是跳转到支付网关。

要激活此功能,您需要联系ČSOB并请求激活此功能。在此期间,您还将收到更详细的技术文档。

功能激活后,您可以调用 payment/checkout 而不是 payment/process

如果您的 merchantId 没有激活此功能,网关将显示错误消息。

$url = $client->getPaymentCheckoutUrl($payment, $oneClickPaymentCheckbox);
redirect($url);

$payment 可以是 Payment 对象或简单的 PayID,与其他方法类似。

对于 $oneClickPaymentCheckbox,请使用在PHPDoc中描述的方法的其中一个选项。

其他参数在方法文档中描述。

感谢 @rootpd。

支付按钮

对于API < 1.8,使用 paymentButton() 方法进行支付,对于API >= 1.8,使用 buttonInit() 方法。这两个方法的详细参数在各自的文档中。

这些方法返回一个包含不同数据的数组,包括 redirect,其中包含要将用户重定向到的地址。因此,不要使用 redirectToGateway() 或类似方法,而是直接将用户重定向到银行返回的地址。

重复支付(一键支付)

从API 1.5开始,可以进行重复支付。具体如何操作,请参阅 ČSOB Wiki。在API 1.9版本中,操作大致如下

  • 您将让客户授权支付模板,方法是在调用 paymentInit() 之前将 Payment 对象的 $payment->setOneClickPayment(true) 设置为 true
  • 然后客户将输入卡号,密码并执行 3D 验证,就像普通支付一样
  • 然后您将保存 PayID,以便您可以引用此授权交易。PayID 将在这里作为某种交易模板ID的作用,您可以稍后无需客户即可重复此交易。
  • 从API版本 1.8开始,存在 paymentOneClickEcho() 用于验证原始 PayID 是否仍然有效。
  • 然后您可以随时调用带有原始交易 PayID 和新 Payment 对象的 paymentOneClickInit() 方法。这将创建新的支付。随后调用 paymentOneClickProcess()(在API 1.8和更早版本中是 paymentOneClickStart())以执行支付
  • 新的支付将获得其自己的 PayID,可以像处理任何其他支付一样处理它
  • 从API 1.9开始,可以通过 $payment->setCustomer()$payment->setOrder() 添加元数据,就像在 paymentInit 中做的那样

日志记录

Client 内置简单日志记录。一个日志用于业务级别消息(例如:“XYZ付款成功”),另一个日志(traceLog)记录与API的详细通信和各种技术细节。

可以简单地输入要记录消息的文件路径,或者提供一个回调函数,将消息转发到应用程序使用的任何日志记录器。可以在Client对象的构造函数或setter中设置日志。

$client->setLog("some/file/log.txt");
$client->setTraceLog(function($message) use ($myLogger) {
	$myLogger->log($message);
});

自定义请求

如果您需要发送请求到本库未特别实现API方法(例如,Masterpass、ApplePay或MallPay方法),可以使用customRequest()方法。只需要注意输入数据的顺序和响应数据在签名中的顺序。

$client->customRequest(

    // URL, jenom konec za společnou adresou API, např. "payment/init"
    $methodUrl,                              
    
    // Array se vstupními daty. Pořadí položek v array je důležité.
    // Na vhodná místa lze vložit prázdné dttm a merchantId, doplní se automaticky.
    $inputPayload,                          
    
    // Array s názvy políček v odpovědi v požadovaném pořadí dle dokumentace.
    // U vnořených objektů a polí lze pracovat s tečkou.
    // Například: array('payId', 'dttm', 'resultCode', 'resultMessage', 'redirect.method', 'redirect.url')
    $expectedOutputFields = array(), 
    
    // Volitelně nějaké extensions
    $extensions = array(), 
    
    $method = "POST",     
    
    // Zalogovat vždy podrobně celou návratovou hodnotu z API?
    $logOutput = false,     
    
    // Pokud z nějakého důvodu selhává ověření podpisu, lze ho takto úplně deaktivovat.
    // Nicméně pak je nutné ručně takovou situaci ošetřit.
    $ignoreInvalidReturnSignature = false
)

$expectedOutputFields中,某个值可以以?开头,在这种情况下,它被视为非必需的,如果银行响应中没有包含,则不会包含在签名基础中。如果没有以?开头,签名基础可以包含空字符串。

扩展

扩展通过Extension类和可选的独立类(目前仅为EET)实现。可以将此类对象附加到每个调用的方法中。然后将附加数据添加到请求中,并自动验证响应签名。

每个扩展都有自己的扩展ID(由银行文档定义)。

如果扩展需要向请求添加更多数据,需要调用setInputData()并作为数组将附加数据传递到请求中。数组中元素的顺序很重要,根据顺序将构造签名字符串和签名。请始终查阅文档,了解参数的顺序,并遵守该顺序。字段dttmextension可以留空(false或null),值将自动填充,但需要将它们放置在数组中的适当位置。

作为替代,可以分离类并实现自己的getRequestSignatureBase()方法,该方法应返回作为签名字符串基础的字符串。

如果扩展向API响应的响应添加数据,可以通过getResponseData()方法访问这些数据。

可以通过setExpectedResponseKeysOrder()设置验证响应签名的验证。向此方法传递一个包含响应字段名称的数组,这些字段在签名字符串中的顺序与签名的顺序相同。或者,可以将Extension分离到自己的类中并实现自己的verifySignature()方法。

如果无法验证响应签名,可以通过setStrictSignatureVerification(false)禁用特定扩展的签名验证。调用API方法后,可以通过isSignatureCorrect()检查签名是否正确,如果不正确,可以自行解决。

对于单个方法调用,可以传递多个扩展,只需将Extension对象数组传递给Client对象的相应参数即可,而不仅仅是单个对象。

日期扩展

如果启用了trxDates扩展,可以在paymentStatus()方法中传递DatesExtension类的对象。调用该方法后,可以从DatesExtension读取所需数据作为DateTime对象。

$extension = new DatesExtension();
$status = $client->paymentStatus($payment, true, $extension);
echo $extension->getCreatedDate()->format("j. n. Y");

可用方法有getCreatedDate()getSettlementDate()getAuthDate(),它们返回DateTime或null,如果响应中没有提及该日期。请注意,settlementDate的精度仅到天,不是到秒。

卡号扩展

如果启用了maskClnRP扩展,可以在paymentStatus()方法中传递CardNumberExtension类的对象。调用该方法后,可以从CardNumberExtension读取卡号的掩码和其过期日期。但请注意,此扩展仅适用于“一键”支付。

$extension = new CardNumberExtension();
$status = $client->paymentStatus($payment, true, $extension);
echo $extension->getMaskedCln() . ' ' . $extension->getExpiration();

可用方法有getMaskedCln()getLongMaskedCln()getExpiration()

EET

EET自API 1.9版本已取消。太好了 😀

如果出于某种原因需要使用,则需要使用API 1.8或1.7。

有问题?

如果您遇到了bug,某个功能不正常或者有改进建议,请添加问题或直接联系我,请放心 :-)