Amazon Pay SDK InvalidSignatureError

2.6k Views Asked by At

I'm integrating Amazon Pay php SDK from documentation, but getting this error. enter image description here

Here's my php implementation code:

$amazonpay_config = array(
    'public_key_id' => 'XXXXXXXX',
    'private_key'   => 'my_private_key_path',
    'region'        => 'US',
    'sandbox'       => true
);
$payload = array(
    'webCheckoutDetails' => array(
        'checkoutReviewReturnUrl' => 'https://www.example.com/review',
        'checkoutResultReturnUrl' => 'https://www.example.com/result'
    ),
    'storeId' => 'amzn1.application-oa2-client.XXXXXXXXX'
);

$headers = array('x-amz-pay-Idempotency-Key' => uniqid());
$requestResult = [
    'error' => 1,
    'msg' => 'Error. Can not create checkout session.',
    'checkoutSession' => null,
    'payloadSign' => null
];

$client = new Client($amazonpay_config);
$resultCheckOut = $client->createCheckoutSession($payload, $headers);
$resultSignPayload = $client->generateButtonSignature($payload);

if($resultCheckOut['status'] !== 201) {
    return json_encode($requestResult, true);
}
else {
    $requestResult = [
        'error' => 0,
        'msg' => null,
        'checkoutSession' => json_decode($resultCheckOut['response']),
        'payloadSign' => $resultSignPayload
    ];
    return $requestResult;
}

Here's JS implementation code for generating Amazon Pay button.

amazon.Pay.renderButton('#amazon-pay-btn', {
    // set checkout environment
    merchantId: 'XXXXXXXX',
    ledgerCurrency: 'USD',
    sandbox: true,
    checkoutLanguage: 'en_US',
    productType: 'PayOnly',
    placement: 'Cart',
    buttonColor: 'Gold',
    createCheckoutSessionConfig: {
        payloadJSON: jsonResult['checkoutSession'], 
        signature: jsonResult['payloadSign'], 
        publicKeyId: 'XXXXXXXXXXX'
    }
});
2

There are 2 best solutions below

2
On BEST ANSWER

Couple of problems with the code, mainly that you aren't passing the payload and signature to the front-end correctly. For the payload, you're using jsonResult['checkoutSession'], while it should be jsonResult['payloadSign']. This doesn't contain the payload though but from the PHP code it's apparently the signature that you have put in there. The full code sample should more like this (not tested).

Back-end:

$headers = array('x-amz-pay-Idempotency-Key' => uniqid());
$requestResult = [
    'error' => 1,
    'msg' => 'Error. Can not create checkout session.',
    'signature' => null,
    'payload' => null
];

$client = new Client($amazonpay_config);
$resultCheckOut = $client->createCheckoutSession($payload, $headers);
$resultSignature = $client->generateButtonSignature($payload);

if($resultCheckOut['status'] !== 201) {
    return json_encode($requestResult, true);
}
else {
    $requestResult = [
        'error' => 0,
        'msg' => null,
        'signature' => $resultSignature,
        'payload' => $payload
    ];
    return json_encode($requestResult);
}

Front-end:

amazon.Pay.renderButton('#amazon-pay-btn', {
    // set checkout environment
    merchantId: 'XXXXXXXX',
    ledgerCurrency: 'USD',
    sandbox: true,
    checkoutLanguage: 'en_US',
    productType: 'PayOnly',
    placement: 'Cart',
    buttonColor: 'Gold',
    createCheckoutSessionConfig: {
        payloadJSON: JSON.stringify(jsonResult['payload']), 
        signature: jsonResult['signature'], 
        publicKeyId: 'XXXXXXXXXXX'
    }
});

I'm not sure how you're passing $requestResult back to the front-end, potentially there's some additional JSON encoding/decoding required to get the right string. To prevent a signature mismatch error, please make sure that the payload string used for the signature generation in the backend, and the payload string assigned to the 'payloadJSON' parameter match exactly (especially pay attention to whitespaces, escape characters, line breaks, etc.).

0
On

Two comments about this issue:

  1. I have defined the payload as an string (that's the way current AmazonPay doc states - Link).
$payload = '{
    "webCheckoutDetails": {
        "checkoutReviewReturnUrl": "https://www.example.com/review",
        "checkoutResultReturnUrl": "https://www.example.com/result"
    },
    "storeId": "amzn1.application-oa2-client.XXXXXXXXX"
}';

instead of array

$payload = array(
    'webCheckoutDetails' => array(
        'checkoutReviewReturnUrl' => 'https://www.example.com/review',
        'checkoutResultReturnUrl' => 'https://www.example.com/result'
    ),
    'storeId' => 'amzn1.application-oa2-client.XXXXXXXXX'
);

The signature was created, but when rendering the button and clicking on it I get the following error.

Error Message: Signature Dk4qznkoiTVqjcY8Yn1l0iLbsoIj2pEAHWVtgYrphLtFXR9BKhJJPD53It4qYOswS1T/STYMHRy5jtCHGqvLntDjuy0MrhkpoHTpYEtwdOqGHA2qk+QnSGV5LoYldQ/UkAxSG7m8s2iOr11q2sWxUjrk2M3fgzAIxDeZRjJYeAr97eGANYva3jtGDfM6cJdieInBM4dEWWxKqGIh6HxOrY5K/ga26494vAwZAGvXRhZG48FOVp/XCr0mbu6V5pkEOzRJSc+hN5WKAs/c49UsfKPx75Ce7QbaBCZZT1UiczfyYx/mBuZuysUlGmnXPhLOLTPw4+SIizH/pOQyClOQyw== does not match signedString AMZN-PAY-RSASSA-PSS dfff7a87b93cfa78685a233f2dd59e18ad0451b2e3a90af11e500fcc0ceee924 for merchant XXXXXXXX

I was some time till I realized that this was the reason of the error. Actually, while writing this, the new lines in the string were the reason. If string is only in one line, it works.

  1. The button only needs the payload and the signed payload. The $client->createCheckoutSession is not needed. More over, the checkoutSessionId of the resultCheckOut is different from the one obtained when the checkoutReviewReturnUrl is called.