W PayU mamy dwa rodzaje uwierzytelniania:
- BasicAuth wymagany jest PosId cient_id i client_secret i nie ma potrzeby tworzenia tokenów i cache-u (prosty i wygodny).
- BearerToken wymagany jest oAuth client_id i client_secret i tu wymagana jest autoryzacja i pobranie bearer tokena (cache).
Tutaj wyskakują blędy autoryzacji gdy nie jest podany oAuth client_id i client_secret w openpayu.
protected function authorizeUrl()
{
if (env('PAYU_SANDBOX', true) == true) {
return 'https://secure.snd.payu.com/pl/standard/user/oauth/authorize';
} else {
return 'https://secure.payu.com/pl/standard/user/oauth/authorize';
}
}
public function authorize() {
$data = [
'grant_type' => 'client_credentials',
'client_id' => env('PAYU_CLIENT_OAUTH_ID', ''),
'client_secret' => env('PAYU_CLIENT_OAUTH_SECRET', ''),
];
// Zwraca bearer token z oAuth clien_id i client_secret
$res = Http::asForm()->withoutRedirecting()->connectTimeout(10)->timeout(60)
->acceptJson()->post($this->authorizeUrl(), $data);
// Cache bearer token here
if ($res->successful()) {
// Json zawiera: access_token, expires_in, grant_type, token_type, refresh_token (nie zawsze jest lepiej pominąć)
return $res->json('access_token');
}
// Dla takiego zapisu
// $res = Http::withHeaders([
// 'Content-Type' => 'application/x-www-form-urlencoded',
// 'Accept' => '*/*',
// ])
// Trzeba dodać
// http_build_query($data, '', '&')
}
public function retrive($id) {
// Get bearer token from cache or from payu
$token = $this->authorize();
// Send with bearer token
$res = Http::withToken($token)->get($this->urlRetrive($id));
// $res = Http::withToken($this->getToken())->get($this->urlOrders(), $order);
// Success 200 or 302
if ($res->ok() || $res->found()) {
return $res->json();
}
}Pobieranie linku do płatności i informacji o płatności. W openpayu gdy nie ma podanago OAuth client_id i client_secret działa tylko BasicAuth, dlatego nie można pobrać bearer tokena ale można pobrać link do płatności (inna metoda uwierzytelniania)!
// Tu działa bez oAuth cient_id i secret_id z json i BasicAuth i nie trzeba podpisywać sygnatury
protected function ordersUrl()
{
if (env('PAYU_SANDBOX', true) == true) {
return 'https://secure.snd.payu.com/api/v2_1/orders';
} else {
return 'https://secure.payu.com/api/v2_1/orders';
}
}
protected function urlRetrive($id)
{
if (env('PAYU_SANDBOX', true) == true) {
return 'https://secure.snd.payu.com/api/v2_1/orders/' . $id;
} else {
return 'https://secure.payu.com/api/v2_1/orders/' . $id;
}
}
// Link do płatności
public function pay()
{
// Dane zamówienia poniżej przykład
$order = [];
// Wystarczy PosId client_secret działa bez oAuth client_secret
$res = Http::withBasicAuth(env('PAYU_CLIENT_ID'), env('PAYU_CLIENT_SECRET'))
->withoutRedirecting()->connectTimeout(10)->timeout(60)
->acceptJson()->post($this->ordersUrl(), $order);
// Success 200 or 302
if ($res->ok() || $res->found()) {
// Json zawiera: redirectUri, orderId, extorderId
return $res->json('redirectUri');
}
// Można i tak
// $res = Http::withHeaders([
// 'Content-Type' => 'application/json',
// 'Accept' => 'application/json',
// 'Authorization' => 'Basic ' . base64_encode(env('PAYU_CLIENT_ID') . ':' . env('PAYU_CLIENT_SECRET'))
// ])->withoutRedirecting()->connectTimeout(10)->timeout(60)->post($this->ordersUrl(), $order);
}
// Informacje o płatności z orderId
public function retrive($id)
{
try {
$res = Http::withBasicAuth(env('PAYU_CLIENT_ID'), env('PAYU_CLIENT_SECRET'))
->withoutRedirecting()->connectTimeout(10)->timeout(60)
->acceptJson()->get($this->urlRetrive($id));
// Success 200 or 302
if ($res->ok() || $res->found()) {
return $res->json();
}
} catch (Throwable $e) {
return response()->json([
'error' => $e->getMessage()
], 422);
}
}
/**
* Curl z openpayu z json dla tworzenia płatności i pobierania danych o zamówieniu.
* Dla autoryzacji trzeba usunąć cześć z json i odkomentować tą
* z application/x-www-form-urlencoded proxy nie jest potrzebne.
*
* @param $requestType
* @param $pathUrl
* @param $data Array
* @return array
*/
public static function curlOrders($url, $data = null)
{
if (empty($url)) {
throw new Exception('The endpoint is empty');
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_HEADER, false);
// Json request
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Basic ' . base64_encode(env('PAYU_CLIENT_ID') . ':' . env('PAYU_CLIENT_SECRET'))
]);
// Form request bearer token for authorization
//curl_setopt($ch, CURLOPT_HTTPHEADER, [
// 'Content-Type: application/x-www-form-urlencoded',
// 'Accept: */*'
//]);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) {
return strlen($header);
});
if ($data) {
// Json request
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
// Form request bearer token for authorization
// curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data, '', '&'));
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_SSLVERSION, 6);
// if ($proxy = self::getProxy()) {
// curl_setopt($ch, CURLOPT_PROXY, $proxy);
// if ($proxyAuth = self::getProxyAuth()) {
// curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyAuth);
// }
// }
$response = curl_exec($ch);
$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return array('code' => $httpStatus, 'response' => trim($response));
}$order['continueUrl'] = 'http://localhost/payment/success'; // Customer will be redirected to this page after successfull payment
$order['notifyUrl'] = 'http://localhost/notify/payu'; // Payu notifications url
$order['extOrderId'] = uniqid(); // Must be unique!
$order['merchantPosId'] = env('PAYU_CLIENT_ID');
$order['customerIp'] = '127.0.0.1';
$order['description'] = 'Products';
$order['currencyCode'] = 'PLN';
$order['totalAmount'] = 3200;
$order['products'][0]['name'] = 'Product1';
$order['products'][0]['unitPrice'] = 1000;
$order['products'][0]['quantity'] = 1;
$order['products'][1]['name'] = 'Product2';
$order['products'][1]['unitPrice'] = 2200;
$order['products'][1]['quantity'] = 1;
//optional section buyer
$order['buyer']['email'] = 'aha@example.com';
$order['buyer']['phone'] = '100100100';
$order['buyer']['firstName'] = 'Alex';
$order['buyer']['lastName'] = 'Black';To niepotrzebne i nie wiem czy działa.
->withOptions([
'curl.options' => [
CURLOPT_ENCODING => 'gzip',
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSLVERSION => 6,
CURLOPT_HEADERFUNCTION => function ($ch, $header) {
return strlen($header);
}
]
])protected ?PayuToken $payuToken = null;
protected function getToken()
{
if ($this->payuToken instanceof PayuToken) {
return $this->payuToken->access_token;
} else {
throw new Exception("Empty token", 422);
}
}
public function authCredentials($grant_type = 'client_credentials')
{
return [
'grant_type' => $grant_type,
'client_id' => env('PAYU_OAUTH_CLIENT_ID', ''),
'client_secret' => env('PAYU_OAUTH_CLIENT_SECRET', ''),
];
}
public function cacheKeyName()
{
return 'payu_token_' . md5(env('PAYU_OAUTH_CLIENT_ID', 'key'));
}
public function authorize()
{
if (Cache::store('file')->has($this->cacheKeyName())) {
$this->payuToken = Cache::store('file')->get($this->cacheKeyName()) ?? null;
if (env('PAYU_SANDBOX', false) == true) {
Log::info('Cached token (sandbox)', [
'token' => $this->payuToken->access_token,
'expires_in' => $this->payuToken->expires_in
]);
}
} else {
// Authorization
$res = Http::asForm()->withoutRedirecting()->connectTimeout(10)->timeout(60)
->acceptJson()->post($this->urlAuthorize(), $this->authCredentials());
if ($res->successful()) {
// Check data
if (
!empty($res->json('expires_in')) &&
!empty($res->json('access_token')) &&
!empty($res->json('grant_type')) &&
!empty($res->json('token_type'))
) {
// Time offset -30min
$expires_in = now()->addSeconds($res->json('expires_in'))->subSeconds(1800);
$this->payuToken = new PayuToken(
$expires_in,
$res->json('access_token'),
$res->json('grant_type'),
$res->json('token_type'),
$res->json('refresh_token') ?? null,
);
Cache::store('file')->put($this->cacheKeyName(), $this->payuToken, $expires_in);
} else {
throw new Exception('Authorization invalid response', 422);
}
} else {
throw new Exception($res->json('error'), $res->status());
}
}
}Po zmianie wersji PHP trzeba pobrać cacert.pem ze strony curla i dodać w php.ini dla curl.cainfo i openssl.cafile inaczej błędy weryfikacji ssl self-signed.
Php:
60: SSL certificate problem: self signed certificate in certificate chain
Payu:
invalid_client - gdy brak podanego client_secret dla OAuth (ten drugi) dlatego zapewne nie można pobrać tokena
i wyskakują błędy autoryzacji (a w dokumentacja coś innego nasmarowane).
{"code":400,"response":"{"status":{"statusCode":"ERROR_SYNTAX",
"code":"103","codeLiteral":"ERROR_SYNTAX","statusDesc":"Bad syntax"}}"}
- a to gdy nie ma json_encode($data) a ustawione jest http_build_query($data,'','&') przy tworzeniu zamówienia.
OpenPayu-Signature (error 103) - Invalid signature do tworzenia orderów nie wymagane,
potrzebna chyba dla www-x-url-encoded lub dla formularzy z hidden="OpenPayu-Signature".