phpcybersource

Creating an invoice through CyberSource APIs using the sandbox credentials is not working


I have created a Shared Secret Key for HTTP Signature Authentication here When I am trying to use them to process a payment in the CyberSource developers playground it works perfectly and in returns 200 OK response

However, when I am trying to create an invoice using the same credentials here, I always get the following 401 UnAuthorized

{
  "submitTimeUtc": "2023-05-25T06:07:10.578Z",
  "status": "UNAUTHORIZED",
  "reason": "Unauthorized",
  "message": "Unauthorized Request"
}

Why and how to solve this issue? Thanks in advance


Solution

  • I have created a service file that can help you, You can use them for the POST and GET both requests

    <?php
    
    
    class CyberSourceService
    {
        public $request_host;
        public $merchant_id;
        public $merchant_key_id;
        public $merchant_secret_key;
    
        public function __construct()
        {
            $this->request_host = 'apitest.cybersource.com' // this is sendbox host
            $this->merchant_id = 'cyber_source.merchant_id'
            $this->merchant_key_id = 'cyber_source.api_key_id'
            $this->merchant_secret_key = 'cyber_source.secret_key'
        }
    
        protected function generateDigest($requestPayload)
        {
            $utf8EncodedString = mb_convert_encoding($requestPayload, "UTF-8");
            $digestEncode = hash("sha256", $utf8EncodedString, true);
    
            return base64_encode($digestEncode);
        }
    
        public function getHttpSignature($resourcePath, $httpMethod = "post", $payload = [])
        {
            $currentDate = date("D, d M Y G:i:s ") . "GMT";
    
            if ($httpMethod == "post") {
                $digest = $this->generateDigest($payload); // Get digest data
    
                $signatureString = "host: " . $this->request_host . "\ndate: " . $currentDate . "\n(request-target): " . $httpMethod . " " . $resourcePath . "\ndigest: SHA-256=" . $digest . "\nv-c-merchant-id: " . $this->merchant_id;
            } else {
                $signatureString = "host: " . $this->request_host . "\ndate: " . $currentDate . "\n(request-target): " . $httpMethod . " " . $resourcePath . "\nv-c-merchant-id: " . $this->merchant_id;
            }
    
            $signatureByteString = mb_convert_encoding($signatureString, "UTF-8");
            $decodeKey = base64_decode($this->merchant_secret_key);
            $signature = base64_encode(hash_hmac("sha256", $signatureByteString, $decodeKey, true));
    
            $signatureHeader = array(
                'keyid="' . $this->merchant_key_id . '"',
                'algorithm="HmacSHA256"'
            );
    
            if ($httpMethod == "post") {
                $signatureHeader = array_merge($signatureHeader, array('headers="host date (request-target) digest v-c-merchant-id"'));
                $signatureHeader = array_merge($signatureHeader, array('signature="' . $signature . '"'));
            } else {
                $signatureHeader = array_merge($signatureHeader, array('headers="host date (request-target) v-c-merchant-id"'));
                $signatureHeader = array_merge($signatureHeader, array('signature="' . $signature . '"'));
            }
    
            $headers = array(
                "Accept: application/hal+json;charset=utf-8",
                "Content-Type: application/json;charset=utf-8",
                "v-c-merchant-id: " . $this->merchant_id,
                "Signature: " . implode(", ", $signatureHeader),
                "Host: " . $this->request_host,
                "Date: " . $currentDate
            );
    
            if ($httpMethod == "post") {
                $headers = array_merge($headers, array("Digest: SHA-256=" . $digest));
            }
    
            return $headers;
        }
    
        public function request($resourcePath, $httpMethod = "post", $payLoad = [])
        {
            $payLoad = json_encode($payLoad);
            $headers = $this->getHttpSignature($resourcePath, $httpMethod, $payLoad);
            $url = "https://" . $this->request_host . $resourcePath;
    
            $curl = curl_init();
    
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    
            if ($httpMethod == "post") {
                curl_setopt($curl, CURLOPT_POST, true);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $payLoad);
            }
    
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_HEADER, 1);
            curl_setopt($curl, CURLOPT_VERBOSE, 0);
    
            $response = curl_exec($curl);
            $http_header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
            $http_body = json_decode(substr($response, $http_header_size), true);
    
            return $http_body ?? [];
        }
    }
    
    

    GET request

    $transactionResponse = (new CyberSourceService())->request("/pts/v2/payments/$paymentID", "get");
    
    

    POST request

     $paymentInformationCardArr = [
                "number" => 'card_number',
                "expirationMonth" => "expiry_month",
                "expirationYear" => "expiry_year",
                "securityCode" => "security_code",
                "type" =>  cardType code //https://developer.cybersource.com/library/documentation/dev_guides/Retail_SO_API/html/Topics/app_card_types.htm
    
    $orderInformationAmountDetailsArr = [
        "totalAmount" => formatPrice($this->ticket->total),
        "currency" => "USD"
    ];
    $orderInformationBillToArr = [
        "firstName" => first_name
        "lastName" => last_name
        "address1" => address1
        "address2" => address2
        "locality" => city
        "administrativeArea" => state
        "country" => country
        "postalCode" => postal_code
        "phoneNumber" => phone_number'
        "email" => $this->user->email,
    ];
    
    $orderPayLoad = [
        "clientReferenceInformation" => [
            "code" => $this->ticket->booking_no
        ],
        "processingInformation" => [
            "commerceIndicator" => "internet",
            "capture" => true
        ],
        "orderInformation" => [
            "billTo" => $orderInformationBillToArr,
            "amountDetails" => $orderInformationAmountDetailsArr
        ],
        "paymentInformation" => [
            "card" => $paymentInformationCardArr
        ]
    ];
    
    $this->orderResponse = (new CyberSourceService())->request("/pts/v2/payments", "post", $orderPayLoad);