Как получить доступ к платежному шлюзу Вьетнама с помощью PHP?

Руководство по использованию PHP для доступа к платежным шлюзам во Вьетнаме

Чтобы получить доступ к вьетнамским платежным шлюзам с помощью PHP, вам нужно выполнить следующие шаги. В качестве примера я использую несколько популярных вьетнамских платежных шлюзов.

Распространенные варианты платежных шлюзов во Вьетнаме

  1. VNPay - Одно из самых популярных решений для локальных платежей
  2. Момо - Платежи с помощью мобильного кошелька
  3. ZaloPay - Еще один популярный электронный кошелек
  4. OnePay - Процессоры Visa/MasterCard

Пример интеграции с VNPay

1. Подготовительная работа

2. Пример кода интеграции PHP

<?php
// config.php - VNPay配置参数
$vnp_Url = "https://sandbox.vnpayment.vn/paymentv2/vpcpay.html";
$vnp_Returnurl = "https://yourdomain.com/return_url.php"; //回调URL
$vnp_TmnCode = "YOUR_MERCHANT_CODE"; //商户号
$secretKey = "YOUR_SECRET_KEY"; //安全密钥

// vnpay_create_payment.php -创建付款请求
function createVNPayPayment($orderId, $amount, $orderInfo) {
global $vnp_Url, $vnp_Returnurl, $vnp_TmnCode, $secretKey;

date_default_timezone_set('Asia/Ho_Chi_Minh');

//准备参数数组
$inputData = array(
"version" => "2",
"command" => "pay",
'merchant' => 'VNPAY',
'tmn_code' => trim($tmnCode),
'create_date' => date('YmdHis'),
'expire_date' => date('YmdHis', strtotime('+15 minutes')),

//订单信息
'order_id' => time(),

//金额(单位为VND)
'amount'=> intval(str_replace(',','',$amount))*100,

//订单描述
'order_desc'=> urlencode($orderInfo),

return_url=> urlencode($return_url),

ip_address=> $_SERVER['REMOTE_ADDR'],

currency=>"VND",

locale=> ($lang == "") ? vn : en,

bank_code=> "",
);

ksort($inputData);

query="";
foreach ($inputData as key value){
if(substr(key0)=="_") continue;
if(!empty(query)) query.= &".key."=".$value; else query=key."=".$value;}

secureHash=hash("sha256", secret_key.query);

inputData["secure_hash"]=$secureHash;

paymentUrl=vpn_URL ."?".http_build_query(input_data);

header("Location: ".payment_url); exit();}

Пример интеграции с кошельком Momo

<?php 
// momo_config.php
define("PARTNER_CODE", "");//Ваш партнерский код от MoMo").
define("ACCESS_KEY",""); ///Ваш ключ доступа от MoMo").
define("SECRET_KEY",""); ///Ваш секретный ключ от MoMo").

function execPostRequest(url data){
ch curl_init();
curl_setopt(ch CURLOPT_URL url).
curl_setopt(ch CURLOPT_POST true).
curl_setopt(ch CURLOPT_POSTFIELDS data).
curl_setopt(ch CURLOPT_USERAGENT $_SERVER['HTTP_USER_AGENT']);
curl_setopt(ch CURLOPT_RETURNTRANSFER true);

result=curl_exec ch );curl_close ch ); return result;}

function createMomoPayment(orderId amount orderInfo callbackUrl redirectUrl){
endpoint = "https://test-payment.momo.vn/gw_payment/transactionProcessor";

requestId time()."" ;//уникальный идентификатор для каждого запроса

rawHash = "partnerCode=" КОД ПАРТНЕРА.
&accessKey ACCESS KEY.
&requestId идентификатор запроса.
&amount сумма.
&orderId Id заказа .
информация о заказе .
return Url callback Url .
уведомить Url callback Url .extra Data "";

подпись hash hmac sha256 raw Hash SECRET KEY)).
массив данных(
Код партнера КОД ПАРТНЕРА ,
Ключ доступа КЛЮЧ ДОСТУПА ,
запрос Id запрос Id ,
сумма сумма ,
ID заказа ID заказа ,

Информация о заказе urldecode(order Info ),

Возврат URL-адреса перенаправления URL-адреса ,

Уведомление о перезвоне URL URl ,

Дополнительные данные "",

Тип запроса захват MO кошелька ",

Подпись Подпись ).

JSON encode(data)).

response exec Post Request(endpoint json encodedata)).

json decode(response true);


if isset(result pay UrL )) header Location result pay UrL ]); else echo Error creating payment ;}? >}?

Пример интеграции с Zalo Pay (похожая структура)

 
класс ZalopayHelper {

const APP_ID - идентификатор вашего приложения ;
const KEY1 Ваш ключ1 ;
const EMBED DATA [" merchantinfo"=> embeddata json encode([" merchant name"=> Your Store Name ])];;

public static function CreateOrder( string description long price string callback url ){
дата по умолчанию часовой пояс установлен Азия Хошимин ).

timestamp round(microtime(true)*1000).

параметры [
appid self APP ID,
apptransid date(Ymd). _. временная метка,# формат yyMMdd временная метка e.g21092740765200

appuser Zalo Pay Demo m desc description,

цена.

элемент JSON encode([]),

Банковский код zalopayapp ,

callbackurl call back ur l ];.

Mac=self Вычислите Mac(params);

params[ mac]=Mac;

response Http Helper post https sb openapi zalopay vn /vp/create params );

Возврат json decode response body ) ;

}

private static function Compute Mac(array params ):string {

orderedParams [].

foreach ([ appid , apptransid , appuser amoun t apptime emb edda ta item ]как param) {

if isset params param]) приказал Params[]=$param.'='.$params[$param];}

сообщение implode(& ordered Params ).

return hash hmac sha256 message self::KEY one false );}}

One Pay International Card Processing (для кредитных карт)

 
класс One PayInternational {

private static endpoint sandbox onep ay international com api/v two /processor ;Â

public static Процесс Платежная карта Номер Срок действия Месяц Срок действия Год cv vAmount Валюта Описание ){
дата по умолчанию часовой пояс установлен Азия Хошимин ).
nonce bin hex случайных байтов шестнадцать )).

post Поля [
номер карты Номер ],
exp month expiry Month ],
exp year expiry Year ],cvv cv v],
currency str toupper currency ),
amoun tround float val Amount *100 ),# в центах/пенсах/etc.

description substr Описание пятьдесят ),
три DSecure false ];

json payload json encode post fields base sixty four encode nonce.json payload)).

signature base sixty four encode hash hmac sha two five six nonce.payload YOUR API SECRE Ttrue)).

заголовки [

Тип содержимого application/json',

Носитель авторизации.

Подпись.подпись ];

ch init();

set opt ch,CUR LOP TSSL VERIFYPEERfalse);

set opt ch,CUR LOP THEADE R headers );

set optch CUR LOP POSTtrue);

set optch CUR LOP POS TFIELDS payload );.

set optch CUR LOP RET UR NTR ANSFE Rtrue).


exec curlexe cch close (ch return response ;}}

? >"

Важный совет:

Весь приведенный выше код является лишь базовой основой, на фактическую реализацию следует обратить внимание:
Должны быть предусмотрены такие меры безопасности, как проверка ввода, обработка ошибок и ведение журнала.
Производственные среды должны использовать HTTPS и хранить конфиденциальные данные в безопасном месте.
Настройте имена параметров и форматы значений в соответствии с последней документацией по API.

Рекомендуется сначала протестировать вашу реализацию в среде "песочницы" каждого платежа.

Подробное руководство по продолжению доступа PHP к платежному шлюзу Вьетнама

Ниже я подробно расскажу о том, как улучшить интеграцию платежей, включая обработку обратных вызовов, меры безопасности и решение распространенных проблем.

1. Реализация обработки обратного вызова VNPay

Когда пользователь завершает платеж, VNPay уведомляет вашу систему двумя способами:

  • перенаправление браузера(return_url)
  • Асинхронное уведомление сервера(IPN - мгновенное уведомление о платеже)

Пример кода return_url.php

<?php
// config.php
require_once('config.php');

// Получите все параметры GET
$vnp_Params = $_GET;
$secureHash = $vnp_Params['vnp_SecureHash'];
unset($vnp_Params['vnp_SecureHash']);

// Сортировка параметров в алфавитном порядке
ksort($vnp_Params).

// Генерируем хэш-значение для аутентификации
$hashData = "";
foreach ($vnp_Params as $key => $value) {
if (substr($key, 0, 4) == "vnp_") {
$hashData . = urlencode($key) . "=" . urlencode($value) . "&";
}
}
$hashData = rtrim($hashData, '&');

$secureSecret = $secretKey; // из файла config.php
$mySecureHash = hash_hmac('sha512', $hashData, $secureSecret);

if ($mySecureHash === $secureHash) {
if ($_GET['vnP_ResponseCode'] == '00') {
// Логика успеха платежа

/*
* :: TODO.
* :: - Обновить статус заказа до оплаченного
* :: -- Запись информации о транзакциях в базу данных
* - vnP_TxnRef - это идентификатор вашего заказа (который вы изначально передали в VNPay).
*/

echo "Успешная оплата заказа: ".$_GET['vnP_TxnRef'];
} else {
// Платеж не прошел или отменен пользователем

/*
* :: TODO.
* - Обновите статус заказа на неудачный/отмененный в вашей базе данных
*/

echo "Платеж не прошел/отменен";
}
} else {
die("Недопустимая подпись").
}

Скрипт обработки IPN (ipn_listener.php)

<?php  
require_once('config.php');

file put contents ('ipn log.txt', print r($_POST true)." \n", FILE APPEND );

если (!empty($_POST)) {
Входные данные=$ POST.
secure Hash=input Data ['vn p Secure Hash'];
unset(input Data ['vn p Secure Hash']);

ksort(входные данные).

i=0;

hash Data="";

foreach (входные данные как ключ-значение){
if substr(key04)=="vn p"){
если strlen(hash Dat a)>0){
hash Dat a.='&'.url encode(key).' ='.url encode(value);
}else{
hash Dat a.=url encode(key).' ='.url encode(value);
}
}
}

check Sum=hash hmac sha512(h ash da ta secret Key );

Если проверить Sum== secure Has h){
транзакция Id=вход Da ta ['vp Txn Ref'].
response Code=input Da ta ['vp Response Co de'];

/* TODO.
1 Убедитесь, что сумма соответствует ожидаемой.
2 Проверьте, не является ли это дубликатом уведомления
3 Обновите свою базу данных соответствующим образом */

switch(response Co de){ case "00": /* SUCCESS */ break ; default : /* FAILURE */ break ; }}else{ http response code(403);//Forbidden exit();}? >

Советы по расширенной интеграции MoMo Wallet

Пример аутентификации с помощью вебхука

 
Функция verify MomoWeb hook(Request ){
Код партнера запрос input('Код партнера').
запрос ключа доступа input('access Key');
request Id time()."" ;//должно совпадать с оригиналом
запрос идентификатора заказа input('orderId');

amount req uest->input amoun t)).
order Info urldecode(req uest->input desc ))).
Время ответа req uest->input resp onse Time )))).
message req uest->input mes sage )))).
trans Id req uest->input transId ))).
error Cod e re quest ->in put errorCode )) ? :0.

extra D ata "";// пользовательские данные, которые вы передали изначально, теперь возвращаются без изменений
raw H ash = "partnerCode={ partnerCo de}&accessKey={ accessKe y}&requestId={ reques tI d}&amount={ amo unt}&orderId={ ord erI d}"." &orderInfo={ orde rIn fo}&responseTime={ res pon seT ime}{ ext raD ata}";".

signatureFromMomo=req est >i nput sig nature );

ourSignature=ha sh hm ac sha256 rawH ash SECRET KEY ).

если наша подпись!= подпись от Момо || errorC ode ! =0){
log info ("Получен недопустимый вебхук MoMo".json encodereq est all())).
abort(400, "Bad Request");}

/* Здесь обрабатывается успешный платеж */

return response json success truemessage OK ]200 headers Content Type application/json ];}

Реализация процесса возврата средств Zalo Pay

 
class ZalopayService{

const REFUND ENDPOINT https sb openapi zalopay vn /vp/refund ;

public static function process Refund(string zp Trans Token long refund Amount string description ){
timestamp round microtime true)*1000 );

params [
appid self APP ID,
zptransid zpTransToken ,
amoun t refundAmount ,
description substr description fifty ),
timestamp timestamp ];

Mac=self Compute Mac params );

params mac]=Mac;

response Http Helper post self REFUND ENDPOINT params );

result json decode response body true );

if isset result return code])&& result return code]==1){
/*
TODO:
Update your database to reflect refund status*/
return true;}else{
Log error ("ZaloPay refund failed".print r(resulttrue));
throw new Exception Failed to process refund with Zalo Pay.);}}

Интеграция 3D Secure для международных карточных платежей One Pay

Для транзакций, требующих 3D-аутентификации:


класс ThreeDSecureHandler {

public static function initiateThreeDSecure(array card Details float amount string currency ){
one pay Client new One PayInternational();

auth Response one pay Client create Auth Request card Details amount currency three DSEnabled=>true]);.

three Ds Method Url auth Response three ds method url ? null.

three Ds Server Trans ID auth Response three ds server trans id ? null.

session put current payment session [ ... .cardDetails ,... authResponse ]);

redirect Url маршрута checkout.threeds challenge name ]).

return view checkout.threeds initiate compact redirectUrl threeDsMethodUrl threeDsServerTransId amount currency cardDetails lastFour digits]) ;)

}

public static function handle Challenge Completion(Request req ){
сеанс Получить текущий сеанс оплаты ;

one pay Client new OnePayInternational();

finalize Result one payClient complete Three DS Auth session get threeds server trans id ],req all());.

/* Проверьте MAC/подпись и т.д.*/

switch finalize Result status]){
дело удалось.
Order Service mark Paid session get order id ]);;
перерыв;
по умолчанию.
Отметка службы заказа Не удалось (сеанс получения идентификатора заказа ]).
выбросьте новое исключение PaymentFailedException();
}}}

Лучшие практики безопасности

Основные меры безопасности.

1.Всегда проверяйте обратные вызовы: Никогда не доверяйте входящим запросам без проверки подписей.

2.Используйте HTTPS везде: Особенно для URL-адресов обратного вызова и при передаче конфиденциальных данных.

3.Реализуйте защиту от CSRF:: На страницах, инициирующих платежи.

4.Продезинфицируйте все входы: Предотвращение атак XSS/SQL-инъекций.

5.Ведите журнал важных событий: Но не записывайте полные данные карты или конфиденциальные токены.

6.Храните минимум данных: Не сохраняйте PAN/CVV после успешного завершения обработки.

7. используйте современные версии TLS (v1.2+).

8.Регулярно меняйте ключи/секреты API, особенно после смены персонала.

9.Следите за подозрительной активностью, например, за несколькими неудачными попытками.

10.Поддерживайте PHP и зависимые компоненты в актуальном состоянии, чтобы избежать известных уязвимостей.

Поиск и устранение неисправностей

Феномен проблемы Возможные причины Решения
Ошибка недействительной/пропущенной подписи Неправильный алгоритм хэширования Несоответствие алгоритма
Проблема с упорядочиванием параметров
Проблема кодирования
Дважды проверьте валидность документов шлюза docs
Убедитесь, что порядок параметров абсолютно одинаков
Тестирование с использованием их выборочных значений, используя тестовые данные для подтверждения
Обратные вызовы не получены Блокирование брандмауэром
Неверный URL-адрес
Временное отключение шлюза Сервис временно недоступен.
Настройки белых списков IP-адресов шлюзов
Проверка URL-адреса в панели управления торговца Проверка бэкэнда торговца
Связаться со службой поддержки Связаться со службой поддержки
Платежи, находящиеся в состоянии ожидания, всегда находятся в состоянии ожидания Асинхронная обработка
Расчетный срок партии Расчетный срок партии Разница в сроках
Подозреваемый в мошенничестве Подождите разумное количество времени, чтобы проверить статус транзакции или связаться со службой поддержки.

Помните, что у каждого вьетнамского платежного оператора могут быть свои требования, поэтому всегда обращайтесь к их последней официальной документации, прежде чем приступать к интеграции. с любой интеграцией.