Метод аутентификации запросов OAuth
OAuth HMAC-SHA1
Для формирования запроса с аутентификацией OAuth HMAC-SHA1 необходимо:
Формирование подписи OAuth HMAC-SHA1
Для подготовки подписи необходимо выполнить следующие действия:
Собрать все параметры тела запроса и все параметры OAuth, включенные в тело запроса (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version):
Значение oauth_consumer_key - логин торговца, предоставленный Платежным Шлюзом, например merchantlogin.Значение oauth_nonce - однократно используемая, сгенерированная случайным образом строка nonce, например y3qlvMPky7g.Значение oauth_signature_method - HMAC-SHA1.Значение oauth_timestamp - текущая временная метка в секундах, например 1669966913.Значение oauth_version - 1.0.Параметр oauth_signature в тело запроса не включен, он включен только в заголовок OAuth.Применить процентное кодирование (percent-encode) к значениям каждого собранного на 1 этапе параметра. Справочная информация содержится в RFC 3986. Незарезервированные символы могут быть закодированы, но не должны кодироваться. Зарезервированные символы должны быть закодированы, например:
%
кодируется в%25
,/
кодируется в%2F
,=
кодируется в%3D
.Сортировать все собранные на этапе 1 параметры в лексикографическом порядке по названиям параметров.
- Соединить с помощью конкатенации отсортированные параметры и их значения с помощью символов & и =:
object1=valueOfObject1&object2=valueOfObject2...
Пример строки соединенных параметров:client-order-id=1234567890&oauth_consumer_key=merchantlogin&oauth_nonce=y3qlvMPky7g&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1669966913&oauth_version=1.0&sending-card-ref-id=7654321
- Применить процентное кодирование к каждому элементу (POST, URL, строка соединенных параметров) и собрать базовую строку для подписи со структурой POST&URL&параметры (в соответствии с OAuth A.5.1.).Пример базовой строки для подписи:
POST&https%3A%2F%2Fgate.payneteasy.com%2Fpaynet%2Fapi%2Fv2%2Fpan-eligibility%2Fsend%2FENDPOINTID&client-order-id%3D1234567890%26oauth_consumer_key%3Dmerchantlogin%26oauth_nonce%3Dy3qlvMPky7g%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1669966913%26oauth_version%3D1.0%26sending-card-ref-id%3D7654321
- Подпись строки с помощью HMAC-SHA1. Ключ подписи является конкатенацией consumer secret (контрольный ключ Торговца) + & + token secret (пустая строка).Пример ключа HMAC-SHA1:
11111111-1111-1111-1111-111111111111&
- Закодировать полученную строку с помощью Base64 для получения подписи.Пример формирования подписи HMAC-SHA1 на Java:
package com.Payneteasy; import org.apache.commons.codec.binary.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; public class HMAC_SHA1 { private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; public static String calculateHMAC(String data, String key) throws NoSuchAlgorithmException, InvalidKeyException { SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); return Base64.encodeBase64String(mac.doFinal(data.getBytes())); } }
Пример подписи:d/IPlITUmPcniwjA7Vckjr6WQeE=
Формирование заголовков OAuth HMAC-SHA1
Запрос с аутентификацией OAuth HMAC-SHA1 должен иметь следующие заголовки:
content-type=application/x-www-form-urlencoded
Authorization: OAuth
Authorization: OAuth
oauth_consumer_key="merchantlogin",
oauth_nonce="y3qlvMPky7g",
oauth_signature="d/IPlITUmPcniwjA7Vckjr6WQeE=",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1669966913",
oauth_version="1.0"
Примечание
Т.к. заголовки запроса обычно не кодируются, значения oauth параметров (особенно oauth_nonce и oauth_signature) должны быть закодированы отдельно (percent-encode) перед добавлением в заголовок Authorization.
Формирование запроса OAuth HMAC-SHA1
Необходимо использовать заголовки из предыдущей секции,
Включить параметры в тело запроса,
Применить процентное кодирование и отправить запрос.
Чтобы сформировать запрос:
Request method: POST
Request URI: https://gate.payneteasy.com/paynet/api/v2/pan-eligibility/send/1111
Headers: Authorization=OAuth realm="",oauth_version="1.0",oauth_consumer_key="merchantlogin",oauth_timestamp="1669966913",oauth_nonce="y3qlvMPky7g",oauth_signature_method="HMAC-SHA1",oauth_signature="d%2FIPlITUmPcniwjA7Vckjr6WQeE%3D"
Accept=*/*
Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1
Body: client-order-id=1234567890&oauth_consumer_key=merchantlogin&oauth_nonce=y3qlvMPky7g&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1669966913&oauth_version=1.0&sending-card-ref-id=7654321
Чтобы сформировать CURL запрос:
curl -H '
Authorization: oauth_version="1.0",
oauth_consumer_key="merchantlogin",
oauth_timestamp="1669966913",
oauth_nonce="y3qlvMPky7g",
oauth_signature_method="HMAC-SHA1",
oauth_signature="d%2FIPlITUmPcniwjA7Vckjr6WQeE%3D"
' --data '
client-order-id=1234567890
&oauth_consumer_key=merchantlogin
&oauth_nonce=y3qlvMPky7g
&oauth_signature_method=HMAC-SHA1
&oauth_timestamp=1669966913
&oauth_version=1.0
&sending-card-ref-id=7654321
' 'https://gate.payneteasy.com/paynet/api/v2/pan-eligibility/send/ENDPOINTID'
OAuth RSA-SHA256
Для формирования запроса с аутентификацией OAuth RSA-SHA256 необходимо:
Генерация ключей
Для отправки запросов с аутентификацией RSA-SHA256, необходимо сгенерировать пару из публичного и приватного ключа:
ПРИВАТНЫЙ ключ для подписания запросов. Этот ключ должен быть защищён от несанкционированного доступа.
ПУБЛИЧНЫЙ ключ для проверки, что запрос был подписан соответствующим приватным ключом. Этот ключ необходимо передать службе поддержки.
Для генерации ключей необходимо скачать последнюю версию утилиты openssl и запустить следующие команды:
openssl genpkey -algorithm RSA -out private_key_pkcs_8.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in private_key_pkcs_8.pem -out public_key.pem
Необходимо использовать разные пары ключей для тестовой и производственной среды во избежание их компрометации.
Для использования инструментов формирования запросов и отладки в документации необходимо использовать ключ в контейнере PKCS#1 (незашифрованный приватный ключ RSA в текстовом формате PKCS#1 PEM). Такой ключ можно получить с помощью следующей команды:
openssl rsa -in private_key_pkcs_8.pem -out private_key_pkcs_1.pem
Для версий OpenSSL v3+ (проверено на v3.0.5) необходима другая команда:
openssl rsa -traditional -in private_key_pkcs_8.pem -out private_key_pkcs_1.pem
Получившийся приватный ключ PKCS#1 RSA будет начинаться со строки —–BEGIN RSA PRIVATE KEY—–. Для производственной среды подходит любой формат ключа, поддерживаемый программным комплексом Присоединяющейся стороны. Для инструментов формирования запросов и отладки в документации подходят только ключи с длиной до 4096. Пример приватного ключа PKCS#1 RSA:
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
Формирование подписи OAuth RSA-SHA256
Для подготовки подписи необходимо выполнить следующие действия:
Собрать все параметры тела запроса и все параметры OAuth, включенные в тело запроса (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version):
Значение oauth_consumer_key - логин торговца, предоставленный Платежным Шлюзом, например merchantlogin.Значение oauth_nonce - однократно используемая, сгенерированная случайным образом строка nonce, например y3qlvMPky7g.Значение oauth_signature_method - RSA-SHA256.Значение oauth_timestamp - текущая временная метка в секундах, например 1669966913.Значение oauth_version - 1.0.Параметр oauth_signature в тело запроса не включен, он включен только в заголовок OAuth.Применить процентное кодирование (percent-encode) к значениям каждого собранного на 1 этапе параметра. Справочная информация содержится в RFC 3986. Незарезервированные символы могут быть закодированы, но не должны кодироваться. Зарезервированные символы должны быть закодированы, например:
%
кодируется в%25
,/
кодируется в%2F
,=
кодируется в%3D
.Сортировать все собранные на этапе 1 параметры в лексикографическом порядке по названиям параметров.
- Соединить с помощью конкатенации отсортированные параметры и их значения с помощью символов & и =:
object1=valueOfObject1&object2=valueOfObject2...
- Применить процентное кодирование к каждому элементу (POST, URL, строка соединенных параметров) и собрать базовую строку для подписи со структурой POST&URL&параметры (в соответствии с OAuth A.5.1.).Пример базовой строки для подписи:
POST&https%3A%2F%2Fgate.payneteasy.com%2Fpaynet%2Fapi%2Fv4%2Ftransfer%2FENDPOINTID&amount%3D10.42%26card_printed_name%3DJohn%2520Doe%26card_recurring_payment_id%3D42322%26client_orderid%3D1%26credit_card_number%3D4210708776705721%26currency%3DUSD%26cvv2%3D123%26deposit2card%3Dfalse%26destination-card-no%3D4232618181101636%26destination_card_recurring_payment_id%3D61622%26expire_month%3D12%26expire_year%3D2099%26ipaddress%3D1.1.1.1.1%26oauth_consumer_key%3Dpaydroid%26oauth_nonce%3DhoFlZri9c17X1Tvb7yD2fsMEQUIWBQ3m%26oauth_signature_method%3DRSA-SHA256%26oauth_timestamp%3D1669720957%26oauth_version%3D1.0%26order_desc%3DYour%2520order%2520description%26redirect_url%3Dhttp%253A%252F%252Fwww.example.com
- Подписать полученную строку для подписи с помощью RSA-SHA256 используя приватный ключ, получение которого описано в предыдущей секции.
- Закодировать полученную строку с помощью Base64 для получения подписи.Пример подписи, закодированной в Base64:
K0hLc7GYh65UDTNvJvJbqoD95T7ekVEwIx+AxLBe2rNndPVzCAZMTi58J5pJlXZA1qOrgzUj/uL764NofP6qrqBXHX9Fpg+PdoMBey7zY9nMOmtdpHhkwyqA0n8e8oh68x+8RtC1+gmaIsIJDVurpCm2CdaViC2ny90GWPrrSin9CFwDmIKBtOJ7dxNnuFQJkvLxwK9JE9gRfQssG4vOrXrn2f5DvENFvFW3fL7meiN3mKuBFyEHIv2cibWopoUTQrxAgCfTHvRuU5nRIct9oWgCYLYzROOPyAIjtyDFKcTnUql9+tD2+p2rMDDU7HqJUGy764rb4ShuvuiuEvzIaNwg3JAxho9fqcKJz5LXt4efX1i8oFt33ztYSgZRojsoW4HCzuhZcQQmRexpmtCGYKqH3Q2BsG2jIkQAxL9BOUOzXNoeXoVQIf3+47cJ0KujEHuDXROblq3o9Uos5K+Mu9Carjs8jHMiBHo4aS4IAgXVY3mEuohCuRhL9/Y9buuyvdKSNsao7qHwD/6bb9Sj2MFFhYP6gHf+p1NipH05224aX9hMZty8Ovb3+ps/wNWYC8NVfft5bh8ETowaHQ5TUOPFdvU+5IkHOnnfbvz1/+jDeErd0Pdq4xH6c5/gZVQY9j6DFpazsw8d3karwXoBduv7I3mh7L3CSjTweABaRMw=
Формирование заголовков OAuth RSA-SHA256
Запрос с аутентификацией OAuth RSA-SHA256 должен иметь следующие заголовки:
content-type=application/x-www-form-urlencoded
Authorization: OAuth
Authorization: OAuth oauth_consumer_key="paydroid", oauth_nonce="hoFlZri9c17X1Tvb7yD2fsMEQUIWBQ3m", oauth_signature="K0hLc7GYh65UDTNvJvJbqoD95T7ekVEwIx%2BAxLBe2rNndPVzCAZMTi58J5pJlXZA1qOrgzUj%2FuL764NofP6qrqBXHX9Fpg%2BPdoMBey7zY9nMOmtdpHhkwyqA0n8e8oh68x%2B8RtC1%2BgmaIsIJDVurpCm2CdaViC2ny90GWPrrSin9CFwDmIKBtOJ7dxNnuFQJkvLxwK9JE9gRfQssG4vOrXrn2f5DvENFvFW3fL7meiN3mKuBFyEHIv2cibWopoUTQrxAgCfTHvRuU5nRIct9oWgCYLYzROOPyAIjtyDFKcTnUql9%2BtD2%2Bp2rMDDU7HqJUGy764rb4ShuvuiuEvzIaNwg3JAxho9fqcKJz5LXt4efX1i8oFt33ztYSgZRojsoW4HCzuhZcQQmRexpmtCGYKqH3Q2BsG2jIkQAxL9BOUOzXNoeXoVQIf3%2B47cJ0KujEHuDXROblq3o9Uos5K%2BMu9Carjs8jHMiBHo4aS4IAgXVY3mEuohCuRhL9%2FY9buuyvdKSNsao7qHwD%2F6bb9Sj2MFFhYP6gHf%2Bp1NipH05224aX9hMZty8Ovb3%2Bps%2FwNWYC8NVfft5bh8ETowaHQ5TUOPFdvU%2B5IkHOnnfbvz1%2F%2BjDeErd0Pdq4xH6c5%2FgZVQY9j6DFpazsw8d3karwXoBduv7I3mh7L3CSjTweABaRMw%3D", oauth_signature_method="RSA-SHA256", oauth_timestamp="1669720957", oauth_version="1.0"
Примечание
Т.к. заголовки запроса обычно не кодируются, значения oauth параметров (особенно oauth_nonce и oauth_signature) должны быть закодированы отдельно (percent-encode) перед добавлением в заголовок Authorization.
Формирование запроса OAuth RSA-SHA256
Чтобы сформировать запрос:
Необходимо использовать заголовки из предыдущей секции,
Включить параметры в тело запроса,
Применить процентное кодирование и отправить запрос.
Чтобы сформировать запрос:
Request method: POST
Request URI: https://gate.payneteasy.com/paynet/api/v4/transfer/4473
Headers: Authorization= OAuth oauth_nonce="C12mCYiI4RDeVTvyZDHS3f0vUbBNrK9G", oauth_signature="S4ahUgep8EV3RTIPdcpO%2FzEwB8V1XwFcGi5zhrYKySPVVA2PAY7AxezPcOCMhMwZQfcf8VOH8O9v5zZ%2BmYV2Qsq4kjPe1zEJIjjdjhI%2B2MX9VW8dWn9DyoTD2lkOYUwGsCteXU6mwGtNergN5KwGTJgqYPfzWFLllSAhGwuOd%2FgHYVRnA6jd4FvywRaRiSsnzgsasGJjWCGzX%2F1B8L78H%2FqD1W7dNJHjC2JbonUiHVT4aOs74qpqYE9zcb5rK8PH1l2ems3ArXhqFaSF85%2BYzJ%2BkSeDswqeIZ1y3NS8XPrmaLeymLNqKpOfAl9Ng47AmIjSYEw3s5fQ7Xi9t4j7Y6fQA4RnIzIdmH4oeMiyzn7dpA87wqnXm5AIm6Den2TJaDg90UoCMXuGHkqfL8GGUSjaWleTOHlk%2FO4dBGDRw4LP1aEaJktQRmT5xwyoaQfz%2Bh2MR7zDpRbVZDpUto1iYlQl5UlgojOHaLLjW1gqggbvrtVXUBT73KcIboWW00VUcbVUX5Yb%2FSu7hUZO2fNAh4LYrsVcXRJxCZuwhXCiqvbR3EziEFDGVEZnKUDLCodzHbVA9hbpxHGa7TvSLFEANCPDFtapMF9eo%2F1yOt5Tkxag4g5yNes93lrVabsTqckiFIfKR2R3CcwJ6MQtMfo8ticJcKxfo2v%2BTkC0bEaRkBGk%3D", oauth_consumer_key="ny_qa_merchant", oauth_signature_method="RSA-SHA256", oauth_timestamp="1669892964", oauth_version="1.0"
Accept=*/*
Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1
Body: cvv2=123&ipaddress=1.1.1.1&amount=15.42&credit_card_number=4210708776705721&client_orderid=1&deposit2card=false&destination-card-no=4232618181101636&expire_month=12&order_desc=Your%20order%20description&card_printed_name=Vasia%20Pupkin¤cy=RUB&expire_year=2099&redirect_url=http%3A%2F%2Fwww.example.com
Чтобы сформировать CURL запрос:
curl -H '
Authorization: OAuth oauth_consumer_key="paydroid",
oauth_nonce="JOARJWjXJgfRUVqig8HIs0ouFWKfK4N5",
oauth_signature="k5Z0XCVdDvb5h873XG6TDMO854PsuueSnSby4h0%2F3j4TKraY6ebjoFfDmndI09%2FQp6uEbCgKregNY5N0ccIVIay49l6v7jMdIFEfqU7E5eu%2BIJcoqG7kMFcdCu29hweYx7p4ZSk%2FUtdGlN3wyUUybCAx73XYoO0tkZteAleyzQlzdpzQ99vPS8FN2WMbNdzU3H2PLf0XOZy4DbAPleZGfu3GWxXe9erGsvzBJozs3WFxiPFeULfzWWsNc1h1P7cnzNbZqXkI%2BV3qiG3jc7tDqGRZP%2BLZFw3nihNlW%2F2Nlp%2FId6QG8kPNOEx2GxAuQa8kufv%2BbpohU8UftZG1SnNfoa4nDVgNbWoSbTXbgXxwHE0ZlccwT6q%2BHbTmgggvIdGN9JuuLUL8fDSCxqu3R7YMvcwArzrkd1XEFUFvYWxHc3QpfbBu0GyLkL7pnmz%2BFPTH48COZ7yXbK6nQDFSIy8lmaEJsnnjMYyMMVgjNkDNzqwNxOQuwprTZ7KzHqWrTj0zHTOHl44q1pJhIqMwxWDPMsziExYbAzxuOkdQFZi%2BK%2Bu3M7tvG5Foy7Vwj%2BDSPMPAhj7j2AmUG4HzaJcpiBWMq0CkGWRjvneOk3NiwkEwOxq5tJOFjsroditUqJQSX1PTqf%2FtuiqaE2Gt5EYl19ZzyXbxtMjGZwL%2BzF1cT6ftTi4%3D",
oauth_signature_method="RSA-SHA256",
oauth_timestamp="1669892687",
oauth_version="1.0"
' --data '
amount=10.42
&card_printed_name=John%20Doe
&card_recurring_payment_id=42322
&client_orderid=1
&credit_card_number=4210708776705721
¤cy=USD
&cvv2=123
&deposit2card=false
&destination-card-no=4232618181101636
&destination_card_recurring_payment_id=61622
&expire_month=12
&expire_year=2099
&ipaddress=1.1.1.1.1
&order_desc=Your%20order%20description
&redirect_url=http%3A%2F%2Fwww.example.com
' 'https://gate.payneteasy.com/paynet/api/v4/transfer/ENDPOINTID'
Формирование запроса OAuth RSA-SHA256
<?php
/**
* OAuth RSA-SHA256 Integration Example for PaynetEasy API
* This example demonstrates a simplified single-file implementation
*/
// Include the OAuth library (tmhOAuth)
class tmhOAuth {
const VERSION = '0.8.5';
var $response = array();
/**
* Creates a new tmhOAuth object
*
* @param string $config, the configuration to use for this request
* @return void
*/
public function __construct($config=array()) {
$this->buffer = null;
$this->reconfigure($config);
$this->reset_request_settings();
$this->set_user_agent();
}
public function reconfigure($config=array()) {
// default configuration options
$this->config = array_merge(
array(
// leave 'user_agent' blank for default, otherwise set this to
// something that clearly identifies your app
'user_agent' => '',
'host' => 'api.twitter.com',
'method' => 'GET',
'consumer_key' => '',
'consumer_secret' => '',
'token' => '',
'secret' => '',
// RSA private key (for RSA-SHA1 and RSA-SHA256 methods)
// Please note that this is expected to be a string representing
// the PEM-formatted key itself and NOT the file name
'private_key_pem' => '',
// OAuth2 bearer token. This should already be URL encoded
'bearer' => '',
// oauth signing variables that are not dynamic
'oauth_version' => '1.0',
'oauth_signature_method' => 'HMAC-SHA1',
// you probably don't want to change any of these curl values
'curl_http_version' => CURL_HTTP_VERSION_1_1,
'curl_connecttimeout' => 30,
'curl_timeout' => 10,
// for security this should always be set to 2.
'curl_ssl_verifyhost' => 2,
// for security this should always be set to true.
'curl_ssl_verifypeer' => true,
// for security this should always be set to true.
'use_ssl' => true,
// you can get the latest cacert.pem from here http://curl.haxx.se/ca/cacert.pem
// if you're getting HTTP 0 responses, check cacert.pem exists and is readable
// without it curl won't be able to create an SSL connection
'curl_cainfo' => __DIR__ . DIRECTORY_SEPARATOR . 'cacert.pem',
'curl_capath' => __DIR__,
// in some cases (very very odd ones) the SSL version must be set manually.
// unless you know why your are changing this, you should leave it as false
// to allow PHP to determine the value for this setting itself.
'curl_sslversion' => false,
'curl_followlocation' => false, // whether to follow redirects or not
// support for proxy servers
'curl_proxy' => false, // really you don't want to use this if you are using streaming
'curl_proxyuserpwd' => false, // format username:password for proxy, if required
'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity etc
// streaming API configuration
'is_streaming' => false,
'streaming_eol' => "\r\n",
'streaming_metrics_interval' => 10,
// header or querystring. You should always use header!
// this is just to help me debug other developers implementations
'as_header' => true,
'force_nonce' => false, // used for checking signatures. leave as false for auto
'force_timestamp' => false, // used for checking signatures. leave as false for auto
),
$config
);
}
private function reset_request_settings($options=array()) {
$this->request_settings = array(
'params' => array(),
'headers' => array(),
'with_user' => true,
'multipart' => false,
);
if (!empty($options))
$this->request_settings = array_merge($this->request_settings, $options);
}
/**
* Sets the useragent for PHP to use
* If '$this->config['user_agent']' already has a value it is used instead of one
* being generated.
*
* @return void value is stored to the config array class variable
*/
private function set_user_agent() {
if (!empty($this->config['user_agent']))
return;
$ssl = ($this->config['curl_ssl_verifyhost'] && $this->config['curl_ssl_verifypeer'] && $this->config['use_ssl']) ? '+' : '-';
$ua = 'tmhOAuth ' . self::VERSION . $ssl . 'SSL - //github.com/themattharris/tmhOAuth';
$this->config['user_agent'] = $ua;
}
/**
* Generates a random OAuth nonce.
* If 'force_nonce' is false a nonce will be generated, otherwise the value of '$this->config['force_nonce']' will be used.
*
* @param string $length how many characters the nonce should be before MD5 hashing. default 12
* @param string $include_time whether to include time at the beginning of the nonce. default true
* @return $nonce as a string
*/
private function nonce($length=12, $include_time=true) {
if ($this->config['force_nonce'] === false) {
$prefix = $include_time ? microtime() : '';
return md5(substr($prefix . uniqid(), 0, $length));
} else {
return $this->config['force_nonce'];
}
}
/**
* Generates a timestamp.
* If 'force_timestamp' is false a timestamp will be generated, otherwise the value of '$this->config['force_timestamp']' will be used.
*
* @return $time as a string
*/
private function timestamp() {
if ($this->config['force_timestamp'] === false) {
$time = time();
} else {
$time = $this->config['force_timestamp'];
}
return (string) $time;
}
/**
* Encodes the string or array passed in a way compatible with OAuth.
* If an array is passed each array value will will be encoded.
*
* @param mixed $data the scalar or array to encode
* @return $data encoded in a way compatible with OAuth
*/
private function safe_encode($data) {
if (is_array($data)) {
return array_map(array($this, 'safe_encode'), $data);
} else if (is_scalar($data)) {
return str_ireplace(
array('+', '%7E'),
array(' ', '~'),
rawurlencode($data)
);
} else {
return '';
}
}
/**
* Decodes the string or array from it's URL encoded form
* If an array is passed each array value will will be decoded.
*
* @param mixed $data the scalar or array to decode
* @return string $data decoded from the URL encoded form
*/
private function safe_decode($data) {
if (is_array($data)) {
return array_map(array($this, 'safe_decode'), $data);
} else if (is_scalar($data)) {
return rawurldecode($data);
} else {
return '';
}
}
/**
* Prepares OAuth1 signing parameters.
*
* @return void all required OAuth parameters, safely encoded, are stored to the class variable '$this->request_settings['oauth1_params']'
*/
private function prepare_oauth1_params() {
$defaults = array(
'oauth_nonce' => $this->nonce(),
'oauth_timestamp' => $this->timestamp(),
'oauth_version' => $this->config['oauth_version'],
'oauth_consumer_key' => $this->config['consumer_key'],
'oauth_signature_method' => $this->config['oauth_signature_method'],
);
// include the user token if it exists
if ( $oauth_token = $this->token() )
$defaults['oauth_token'] = $oauth_token;
$this->request_settings['oauth1_params'] = array();
// safely encode
foreach ($defaults as $k => $v) {
$this->request_settings['oauth1_params'][$this->safe_encode($k)] = $this->safe_encode($v);
}
}
private function token() {
if ( $this->request_settings['with_user'] ) {
if (isset($this->config['token']) && !empty($this->config['token'])) return $this->config['token'];
elseif (isset($this->config['user_token'])) return $this->config['user_token'];
}
return '';
}
private function secret() {
if ( $this->request_settings['with_user'] ) {
if (isset($this->config['secret']) && !empty($this->config['secret'])) return $this->config['secret'];
elseif (isset($this->config['user_secret'])) return $this->config['user_secret'];
}
return '';
}
/**
* Extracts and decodes OAuth parameters from the passed string
*
* @param string $body the response body from an OAuth flow method
* @return array the response body safely decoded to an array of key => values
*/
public function extract_params($body) {
$kvs = explode('&', $body);
$decoded = array();
foreach ($kvs as $kv) {
$kv = explode('=', $kv, 2);
$kv[0] = $this->safe_decode($kv[0]);
$kv[1] = $this->safe_decode($kv[1]);
$decoded[$kv[0]] = $kv[1];
}
return $decoded;
}
/**
* Prepares the HTTP method for use in the base string by converting it to
* uppercase.
*
* @return void value is stored to the class variable '$this->request_settings['method']'
*/
private function prepare_method() {
$this->request_settings['method'] = strtoupper($this->request_settings['method']);
}
/**
* Prepares the URL for use in the base string by ripping it apart and
* reconstructing it.
*
* Ref: 3.4.1.2
*
* @return void value is stored to the class array variable '$this->request_settings['url']'
*/
private function prepare_url() {
$parts = parse_url($this->request_settings['url']);
$port = isset($parts['port']) ? $parts['port'] : false;
$scheme = $parts['scheme'];
$host = $parts['host'];
$path = isset($parts['path']) ? $parts['path'] : false;
$port or $port = ($scheme == 'https') ? '443' : '80';
if (($scheme == 'https' && $port != '443') || ($scheme == 'http' && $port != '80')) {
$host = "$host:$port";
}
// the scheme and host MUST be lowercase
$this->request_settings['url'] = strtolower("$scheme://$host");
// but not the path
$this->request_settings['url'] .= $path;
}
/**
* If the request uses multipart, and the parameter isn't a file path, prepend a space
* otherwise return the original value. we chose a space here as twitter whitespace trims from
* the beginning of the tweet. we don't use \0 here because it's the character for string
* termination.
*
* @param the parameter value
* @return string the original or modified string, depending on the request and the input parameter
*/
private function multipart_escape($value) {
if (!$this->request_settings['multipart'] || strpos($value, '@') !== 0)
return $value;
// see if the parameter is a file.
// we split on the semi-colon as it's the delimiter used on media uploads
// for fields with semi-colons this will return the original string
list($file) = explode(';', substr($value, 1), 2);
if (file_exists($file))
return $value;
return " $value";
}
/**
* Prepares all parameters for the base string and request.
* Multipart parameters are ignored as they are not defined in the specification,
* all other types of parameter are encoded for compatibility with OAuth.
*
* @param array $params the parameters for the request
* @return void prepared values are stored in the class array variable '$this->request_settings'
*/
private function prepare_params() {
$doing_oauth1 = false;
$this->request_settings['prepared_params'] = array();
$prepared = &$this->request_settings['prepared_params'];
$prepared_pairs = array();
$prepared_pairs_with_oauth = array();
if (isset($this->request_settings['oauth1_params'])) {
$oauth1 = &$this->request_settings['oauth1_params'];
$doing_oauth1 = true;
$params = array_merge($oauth1, $this->request_settings['params']);
// Remove oauth_signature if present
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
unset($params['oauth_signature']);
// empty the oauth1 array. we reset these values later in this method
$oauth1 = array();
} else {
$params = $this->request_settings['params'];
}
// Parameters are sorted by name, using lexicographical byte value ordering.
// Ref: Spec: 9.1.1 (1)
uksort($params, 'strcmp');
// set this now so we're not doing it on every parameter
$supports_curl_file = class_exists('CurlFile', false);
// encode params unless we're doing multipart
foreach ($params as $k => $v) {
$k = $this->request_settings['multipart'] ? $k : $this->safe_encode($k);
if (is_array($v))
$v = implode(',', $v);
// we don't need to do the multipart escaping if we support curlfile
if ($supports_curl_file && ($v instanceof CurlFile)) {
// leave $v alone
} elseif ($this->request_settings['multipart']) {
$v = $this->multipart_escape($v);
} else {
$v = $this->safe_encode($v);
}
// split parameters for the basestring and authorization header, and recreate the oauth1 array
if ($doing_oauth1) {
// if we're doing multipart, only store the oauth_* params, ignore the users request params
if ((strpos($k, 'oauth') === 0) || !$this->request_settings['multipart'])
$prepared_pairs_with_oauth[] = "{$k}={$v}";
if (strpos($k, 'oauth') === 0) {
$oauth1[$k] = $v;
continue;
}
}
$prepared[$k] = $v;
if (!$this->request_settings['multipart'])
$prepared_pairs[] = "{$k}={$v}";
}
if ($doing_oauth1) {
$this->request_settings['basestring_params'] = implode('&', $prepared_pairs_with_oauth);
}
// setup params for GET/POST/PUT method handling
if (!empty($prepared)) {
$content = implode('&', $prepared_pairs);
switch ($this->request_settings['method']) {
case 'PUT':
// fall through to POST as PUT should be treated the same
case 'POST':
$this->request_settings['postfields'] = $this->request_settings['multipart'] ? $prepared : $content;
break;
default:
$this->request_settings['querystring'] = $content;
break;
}
}
}
/**
* Prepares the OAuth signing key
*
* @return void prepared signing key is stored in the class variable 'signing_key'
*/
private function prepare_signing_key() {
$left = $this->safe_encode($this->config['consumer_secret']);
$right = $this->safe_encode($this->secret());
$this->request_settings['signing_key'] = $left . '&' . $right;
}
/**
* Prepare the base string.
* Ref: Spec: 9.1.3 ("Concatenate Request Elements")
*
* @return void prepared base string is stored in the class variable 'base_string'
*/
private function prepare_base_string() {
$url = $this->request_settings['url'];
# if the host header is set we need to rewrite the basestring to use
# that, instead of the request host. otherwise the signature won't match
# on the server side
if (!empty($this->request_settings['headers']['Host'])) {
$url = str_ireplace(
$this->config['host'],
$this->request_settings['headers']['Host'],
$url
);
}
$base = array(
$this->request_settings['method'],
$url,
$this->request_settings['basestring_params']
);
$this->request_settings['basestring'] = implode('&', $this->safe_encode($base));
}
/**
* Signs the OAuth 1 request
*
* @return void oauth_signature is added to the parameters in the class array variable '$this->request_settings'
*/
private function prepare_oauth_signature() {
switch ($this->config['oauth_signature_method']) {
case 'HMAC-SHA1':
$signature = $this->sign_with_hmac('sha1');
break;
case 'HMAC-SHA256':
$signature = $this->sign_with_hmac('sha256');
break;
case 'RSA-SHA1':
$signature = $this->sign_with_rsa(OPENSSL_ALGO_SHA1);
break;
case 'RSA-SHA256':
$signature = $this->sign_with_rsa(OPENSSL_ALGO_SHA256);
break;
default:
throw new Exception("Unsupported oauth_signature_method: '" . $this->config['oauth_signature_method'] . "'");
}
$this->request_settings['oauth1_params']['oauth_signature'] = $this->safe_encode(base64_encode($signature));
}
/**
* Signs the OAuth 1 request using HMAC-based signature algorithm
*
* @param string $algorithm algorithm name (like sha1 or sha256)
* @return binary signature
*/
private function sign_with_hmac($algorithm) {
return hash_hmac(
$algorithm, $this->request_settings['basestring'], $this->request_settings['signing_key'], true
);
}
/**
* Signs the OAuth 1 request using RSA-based signature algorithm
*
* @param mixed $algorithm ID or name of hash algorithm that will be
* used to compute base string hash before encrypting it with RSA;
* values understood by openssl_sign()'s $signature_alg parameter
* are accepted here (like 'sha1' or OPENSSL_ALGO_SHA256)
* @return binary signature
*/
private function sign_with_rsa($algorithm) {
if (!function_exists('openssl_sign')) {
throw new Exception("openssl_sign function does not exist. Please make sure Openssl extension is installed");
}
if ($this->config['private_key_pem'] == '') {
throw new Exception("No private key PEM is configured, cannot sign");
}
$ok = openssl_sign($this->request_settings['basestring'], $signature, $this->config['private_key_pem'], $algorithm);
if (!$ok) {
throw new Exception("Cannot sign: " . openssl_error_string());
}
return $signature;
}
/**
* Prepares the Authorization header
*
* @return void prepared authorization header is stored in the class variable headers['Authorization']
*/
private function prepare_auth_header() {
if (!$this->config['as_header'])
return;
// oauth1
if (isset($this->request_settings['oauth1_params'])) {
// sort again as oauth_signature was added post param preparation
uksort($this->request_settings['oauth1_params'], 'strcmp');
$encoded_quoted_pairs = array();
foreach ($this->request_settings['oauth1_params'] as $k => $v) {
$encoded_quoted_pairs[] = "{$k}=\"{$v}\"";
}
$header = 'OAuth ' . implode(', ', $encoded_quoted_pairs);
} elseif (!empty($this->config['bearer'])) {
$header = 'Bearer ' . $this->config['bearer'];
}
if (isset($header))
$this->request_settings['headers']['Authorization'] = $header;
}
/**
* Create the bearer token for OAuth2 requests from the consumer_key and consumer_secret.
*
* @return string the bearer token
*/
public function bearer_token_credentials() {
$credentials = implode(':', array(
$this->safe_encode($this->config['consumer_key']),
$this->safe_encode($this->config['consumer_secret'])
));
return base64_encode($credentials);
}
/**
* Make an HTTP request using this library. This method doesn't return anything.
* Instead the response should be inspected directly.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs. Default empty array
* @param string $useauth whether to use authentication when making the request. Default true
* @param string $multipart whether this request contains multipart data. Default false
* @param array $headers any custom headers to send with the request. Default empty array
* @return int the http response code for the request. 0 is returned if a connection could not be made
*/
public function request($method, $url, $params=array(), $useauth=true, $multipart=false, $headers=array()) {
$options = array(
'method' => $method,
'url' => $url,
'params' => $params,
'with_user' => true,
'multipart' => $multipart,
'headers' => $headers
);
$options = array_merge($this->default_options(), $options);
if ($useauth) {
return $this->user_request($options);
} else {
return $this->unauthenticated_request($options);
}
}
public function apponly_request($options=array()) {
$options = array_merge($this->default_options(), $options, array(
'with_user' => false,
));
$this->reset_request_settings($options);
if ($options['without_bearer']) {
return $this->oauth1_request();
} else {
$this->prepare_method();
$this->prepare_url();
$this->prepare_params();
$this->prepare_auth_header();
return $this->curlit();
}
}
public function user_request($options=array()) {
$options = array_merge($this->default_options(), $options, array(
'with_user' => true,
));
$this->reset_request_settings($options);
return $this->oauth1_request();
}
public function unauthenticated_request($options=array()) {
$options = array_merge($this->default_options(), $options, array(
'with_user' => false,
));
$this->reset_request_settings($options);
$this->prepare_method();
$this->prepare_url();
$this->prepare_params();
return $this->curlit();
}
/**
* Signs the request and adds the OAuth signature. This runs all the request
* parameter preparation methods.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs
* @param boolean $with_user whether to include the user credentials when making the request.
* @return void
*/
private function oauth1_request() {
$this->prepare_oauth1_params();
$this->prepare_method();
$this->prepare_url();
$this->prepare_params();
$this->prepare_base_string();
$this->prepare_signing_key();
$this->prepare_oauth_signature();
$this->prepare_auth_header();
return $this->curlit();
}
private function default_options() {
return array(
'method' => 'GET',
'params' => array(),
'with_user' => true,
'multipart' => false,
'headers' => array(),
'without_bearer' => false,
);
}
/**
* Make a long poll HTTP request using this library. This method is
* different to the other request methods as it isn't supposed to disconnect
*
* Using this method expects a callback which will receive the streaming
* responses.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs
* @param string $callback the callback function to stream the buffer to.
* @return void
*/
public function streaming_request($method, $url, $params=array(), $callback='') {
if ( ! empty($callback) ) {
if ( ! is_callable($callback) ) {
return false;
}
$this->config['streaming_callback'] = $callback;
}
$this->metrics['start'] = time();
$this->metrics['interval_start'] = $this->metrics['start'];
$this->metrics['messages'] = 0;
$this->metrics['last_messages'] = 0;
$this->metrics['bytes'] = 0;
$this->metrics['last_bytes'] = 0;
$this->config['is_streaming'] = true;
$this->request($method, $url, $params);
}
/**
* Handles the updating of the current Streaming API metrics.
*
* @return array the metrics for the streaming api connection
*/
private function update_metrics() {
$now = time();
if (($this->metrics['interval_start'] + $this->config['streaming_metrics_interval']) > $now)
return null;
$this->metrics['mps'] = round( ($this->metrics['messages'] - $this->metrics['last_messages']) / $this->config['streaming_metrics_interval'], 2);
$this->metrics['bps'] = round( ($this->metrics['bytes'] - $this->metrics['last_bytes']) / $this->config['streaming_metrics_interval'], 2);
$this->metrics['last_bytes'] = $this->metrics['bytes'];
$this->metrics['last_messages'] = $this->metrics['messages'];
$this->metrics['interval_start'] = $now;
return $this->metrics;
}
/**
* Utility function to create the request URL in the requested format.
* If a fully-qualified URI is provided, it will be returned.
* Any multi-slashes (except for the protocol) will be replaced with a single slash.
*
*
* @param string $request the API method without extension
* @param string $extension the format of the response. Default json. Set to an empty string to exclude the format
* @return string the concatenation of the host, API version, API method and format, or $request if it begins with http
*/
public function url($request, $extension='json') {
// remove multi-slashes
$request = preg_replace('$([^:])//+$', '$1/', $request);
if (stripos($request, 'http') === 0 || stripos($request, '//') === 0) {
return $request;
}
$extension = strlen($extension) > 0 ? ".$extension" : '';
$proto = $this->config['use_ssl'] ? 'https:/' : 'http:/';
// trim trailing slash
$request = ltrim($request, '/');
$pos = strlen($request) - strlen($extension);
if (substr($request, $pos) === $extension)
$request = substr_replace($request, '', $pos);
return implode('/', array(
$proto,
$this->config['host'],
$request . $extension
));
}
/**
* Public access to the private safe decode/encode methods
*
* @param string $text the text to transform
* @param string $mode the transformation mode. either encode or decode
* @return string $text transformed by the given $mode
*/
public function transformText($text, $mode='encode') {
return $this->{"safe_$mode"}($text);
}
/**
* Utility function to parse the returned curl headers and store them in the
* class array variable.
*
* @param object $ch curl handle
* @param string $header the response headers
* @return string the length of the header
*/
private function curlHeader($ch, $header) {
$this->response['raw'] .= $header;
list($key, $value) = array_pad(explode(':', $header, 2), 2, null);
$key = trim($key);
$value = trim($value);
if ( ! isset($this->response['headers'][$key])) {
$this->response['headers'][$key] = $value;
} else {
if (!is_array($this->response['headers'][$key])) {
$this->response['headers'][$key] = array($this->response['headers'][$key]);
}
$this->response['headers'][$key][] = $value;
}
return strlen($header);
}
/**
* Utility function to parse the returned curl buffer and store them until
* an EOL is found. The buffer for curl is an undefined size so we need
* to collect the content until an EOL is found.
*
* This function calls the previously defined streaming callback method.
*
* @param object $ch curl handle
* @param string $data the current curl buffer
* @return int the length of the data string processed in this function
*/
private function curlWrite($ch, $data) {
$l = strlen($data);
if (strpos($data, $this->config['streaming_eol']) === false) {
$this->buffer .= $data;
return $l;
}
$buffered = explode($this->config['streaming_eol'], $data);
$content = $this->buffer . $buffered[0];
$this->metrics['messages']++;
$this->metrics['bytes'] += strlen($content);
if ( ! is_callable($this->config['streaming_callback']))
return 0;
$metrics = $this->update_metrics();
$stop = call_user_func(
$this->config['streaming_callback'],
$content,
strlen($content),
$metrics
);
$this->buffer = $buffered[1];
if ($stop)
return 0;
return $l;
}
/**
* Makes a curl request. Takes no parameters as all should have been prepared
* by the request method
*
* the response data is stored in the class variable 'response'
*
* @return int the http response code for the request. 0 is returned if a connection could not be made
*/
private function curlit() {
$this->response = array(
'raw' => ''
);
// configure curl
$c = curl_init();
if ($this->request_settings['method'] == 'GET' && isset($this->request_settings['querystring'])) {
$this->request_settings['url'] = $this->request_settings['url'] . '?' . $this->request_settings['querystring'];
} elseif ($this->request_settings['method'] == 'POST' || $this->request_settings['method'] == 'PUT') {
$postfields = array();
if (isset($this->request_settings['postfields']))
$postfields = $this->request_settings['postfields'];
curl_setopt($c, CURLOPT_POSTFIELDS, $postfields);
}
curl_setopt($c, CURLOPT_CUSTOMREQUEST, $this->request_settings['method']);
curl_setopt_array($c, array(
CURLOPT_HTTP_VERSION => $this->config['curl_http_version'],
CURLOPT_USERAGENT => $this->config['user_agent'],
CURLOPT_CONNECTTIMEOUT => $this->config['curl_connecttimeout'],
CURLOPT_TIMEOUT => $this->config['curl_timeout'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => $this->config['curl_ssl_verifypeer'],
CURLOPT_SSL_VERIFYHOST => $this->config['curl_ssl_verifyhost'],
CURLOPT_FOLLOWLOCATION => $this->config['curl_followlocation'],
CURLOPT_PROXY => $this->config['curl_proxy'],
CURLOPT_ENCODING => $this->config['curl_encoding'],
CURLOPT_URL => $this->request_settings['url'],
// process the headers
CURLOPT_HEADERFUNCTION => array($this, 'curlHeader'),
CURLOPT_HEADER => false,
CURLINFO_HEADER_OUT => true,
));
if ($this->config['curl_cainfo'] !== false)
curl_setopt($c, CURLOPT_CAINFO, $this->config['curl_cainfo']);
if ($this->config['curl_capath'] !== false)
curl_setopt($c, CURLOPT_CAPATH, $this->config['curl_capath']);
if ($this->config['curl_proxyuserpwd'] !== false)
curl_setopt($c, CURLOPT_PROXYUSERPWD, $this->config['curl_proxyuserpwd']);
if ($this->config['curl_sslversion'] !== false)
curl_setopt($c, CURLOPT_SSLVERSION, $this->config['curl_sslversion']);
if ($this->config['is_streaming']) {
// process the body
$this->response['content-length'] = 0;
curl_setopt($c, CURLOPT_TIMEOUT, 0);
curl_setopt($c, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
}
if ( ! empty($this->request_settings['headers'])) {
foreach ($this->request_settings['headers'] as $k => $v) {
$headers[] = trim($k . ': ' . $v);
}
curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
}
if (isset($this->config['block']) && (true === $this->config['block']))
return 0;
// do it!
$response = curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
$info = curl_getinfo($c);
$error = curl_error($c);
$errno = curl_errno($c);
curl_close($c);
// store the response
$this->response['code'] = $code;
$this->response['response'] = $response;
$this->response['info'] = $info;
$this->response['error'] = $error;
$this->response['errno'] = $errno;
if (!isset($this->response['raw'])) {
$this->response['raw'] = '';
}
$this->response['raw'] .= $response;
return $code;
}
}
/*
* Configuration - Replace these values with your actual credentials
*/
$config = [
'server_host' => 'sandbox.payneteasy.com',
'endpoint_id' => 1, // Your endpoint ID
'merchant_login' => 'your-merchant-login',
'private_key_file' => 'test-private-key.pem', // Your private key file
'test_mode' => true // Set to false for production
];
/*
* API Request Function
*/
function makeApiRequest($endpoint, $params, $config) {
// Load private key
$private_key_pem = file_get_contents($config['private_key_file']);
if (!$private_key_pem) {
throw new Exception("Could not load private key file");
}
// Initialize OAuth
$oauth = new tmhOAuth();
$oauth->reconfigure([
'consumer_key' => $config['merchant_login'],
'oauth_signature_method' => 'RSA-SHA256',
'private_key_pem' => $private_key_pem,
'curl_ssl_verifypeer' => $config['test_mode'] ? false : true
]);
// Make request
$url = "https://{$config['server_host']}/paynet/api/v4/{$endpoint}/{$config['endpoint_id']}";
$status = $oauth->request('POST', $url, $params);
// Handle response
if ($status == 0) {
throw new Exception('HTTP request failed');
} elseif ($status != 200) {
throw new Exception("HTTP error: {$status}");
}
return [
'status' => $status,
'response' => $oauth->response['response'],
'headers' => $oauth->response['headers']
];
}
/*
* Example Usage - Transfer by Reference
*/
if (isset($_POST['action']) && $_POST['action'] == 'transfer') {
try {
$params = [
'client-order-id' => 'transfer-' . uniqid(),
'amount' => $_POST['amount'] ?? '100',
'currency' => $_POST['currency'] ?? 'RUB',
'order_desc' => $_POST['description'] ?? 'Consumer credit',
'destination-card-no' => $_POST['card_number'] ?? '4444333322221111'
];
$result = makeApiRequest('transfer-by-ref', $params, $config);
$response = json_decode($result['response'], true);
echo "<div class='alert alert-success'>Success! Transaction ID: " .
($response['transaction-id'] ?? 'N/A') . "</div>";
} catch (Exception $e) {
echo "<div class='alert alert-danger'>Error: " . $e->getMessage() . "</div>";
}
}
/*
* Example Usage - Bank Payout
*/
if (isset($_POST['action']) && $_POST['action'] == 'payout') {
try {
$params = [
'client-order-id' => 'payout-' . uniqid(),
'amount' => $_POST['amount'] ?? '100',
'currency' => $_POST['currency'] ?? 'USD',
'order_desc' => $_POST['description'] ?? 'Bank payout',
'account_number' => $_POST['account_number'] ?? '123412341234',
'bank_name' => $_POST['bank_name'] ?? 'Test Bank'
];
$result = makeApiRequest('payout', $params, $config);
$response = json_decode($result['response'], true);
echo "<div class='alert alert-success'>Success! Payout ID: " .
($response['payout-id'] ?? 'N/A') . "</div>";
} catch (Exception $e) {
echo "<div class='alert alert-danger'>Error: " . $e->getMessage() . "</div>";
}
}
?>