Skip to content

Latest commit

 

History

History
339 lines (283 loc) · 10.3 KB

File metadata and controls

339 lines (283 loc) · 10.3 KB

Payu Autoryzacja

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).

OAuth Bearer Token

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();
	}
}

BasicAuth

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));
}

Dane zamówienia

$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';

Http client options

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);
        }
    ]
])

Authorization with Cache

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());
		}
	}
}

Payu errors

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".