0

私はこの流れに従うペイパルエクスプレスチェックアウトを使用しています: ここに画像の説明を入力してください

彼らがペイパルのウェブサイトで提出するとき、それは確認ボタンで注文レビューを示す私のサイトのリターンURLに従います、そして、2つのGET変数はペイパルから返されます:tokenpayerId。トークンは、配送情報を要求し、後で支払いを確定する許可を与えてくれます。

最初の問題は、アドレスバーにURLを入力して「paypalでのチェックアウト」ページに直接アクセスできることです。$_SESSION['Payment_Amount']変数が設定されていない場合は、支払い金額を0として処理し、エラー。

SetExpressCheckoutAPI呼び出しが失敗しました。詳細なエラーメッセージ:このトランザクションは処理できません。請求額はゼロです。

カートページに別のセッション変数を設定して、最初にカートにアクセスし、確認後に変数をクリアできることはわかっていますが、ユーザーがカートページにアクセスする必要があるのは一度だけで、変数はトークンリクエストをペイパルに送信する機密ページにアクセスできるように設定します。

次の問題は、すべての手順を実行し、ユーザーが[注文の確認]ボタンを押した後、その注文/お金を処理するためにリクエストがペイパルに送信されることtokenです。ユーザーはページの[戻る]ボタンを押して注文レビューをもう一度確認できます。次に、ユーザーは注文の確認をもう一度押すと、そのトークンの注文が既に処理されたことを示すエラーが表示されます。

GetExpressCheckoutDetailsAPI呼び出しが失敗しました。詳細なエラーメッセージ:このトークンのトランザクションはすでに成功しています。

これは明らかに良いことですが、ユーザーが機密性の高いページにアクセスできないようにするには、何を実装する必要がありますか?バックエンドデータベースで特定のキーを追跡する必要がありますか?

現在、私はペイパルのサンドボックスでローカルホストに取り組んでいます。

4

1 に答える 1

1

ユーザーが必要な手順を正しい順序で実行し、ユーザーがこの順序から飛び出さないようにするプロセスを作成する必要があります。

ユーザー セッションの歩数を追跡することは、当然のことのように思えます。セッションが要求されたステップを許可しない場合は、paypal に尋ねる代わりに、他の場所にリダイレクトします。

デラックス バージョンでは、後で簡単に改善できるようにステート マシンを実装します。ステート マシンには、最初は大きなオーバーヘッドのように見えるという欠点があり、最初に別のアプローチをとった場合、後で実装するのは面倒です。そのため、最初から使用を検討することが重要です。

後で別の支払いプロバイダーを追加したい場合はどうすればよいですか? このためにステート マシンを簡単に拡張できます。

編集: 実際、ユーザーがサイトに戻った後にペイパルが送信することを期待しているのは、請求したい金額だけです。この情報は、paypal に送信するリターン URL に入力することで渡すことができます。データエラーや簡単な改ざんを防ぐために、そこにチェックサムを追加してみてください (それでも、金額が正しくない場合、Paypal はプロセスを失敗させます)。基本的には完了です。セッションはまったく必要ありません。

Edit2: これは、paypals の最初のステップの nvp パラメータを定義する私のコードの抜粋です。内部にも必要な認証が必要です。

public function preparePayment(...) {
        $nvp = array(

        'METHOD'    => 'SetExpressCheckout',
        'VERSION'   => '52.0',

        'RETURNURL'     => 'https://'.$request->server['HTTP_HOST'].'/'.$request->getLanguage().'/paypal/success/'.$this->hashAmount($amount),
        'CANCELURL'     => 'https://'.$request->server['HTTP_HOST'].'/'.$request->getLanguage().'/paypal/cancel',

            'CURRENCYCODE'  => $amount->getCurrency(),
            'AMT'           => number_format($amount->getAmount(), 2, '.', ''),
            'ITEMAMT'       => number_format($amount->getNettoAmount(), 2, '.', ''),
            'TAXAMT'        => number_format($amount->getVatAmount(), 2, '.', ''),
            'PAYMENTACTION' => 'Sale',
            'LOCALECODE'    => strtoupper($request->getLanguage())
        );
}
protected function hashAmount(Currency_Class $amount) {
    return urlencode(
        sprintf(
            '%s-%s-%s-%u',
            number_format($amount->getNettoAmount(), 2, '', ''),
            number_format($amount->getVatAmount(), 2, '', ''),
            strtoupper($amount->getCurrency()),
            $this->makeChecksumString(number_format($amount->getNettoAmount(), 2, '', ''), strtoupper($amount->getCurrency()))
        )
    );
}

protected function makeChecksumString($amount, $currency) {
    return crc32(sprintf('%sSaltValue%s', $amount, $currency));
}

protected function dehashAmount($string) {
    $parts = array();
    $found = preg_match('/^(\d+)\-(\d+)\-([A-Z]+)\-(\d+)$/', $string, $parts);
    if ($found) {
        $check = sprintf('%u', $this->makeChecksumString($parts[1], $parts[3]));
        if ($check == $parts[4]) {
            $netto  = floatval(substr($parts[1], 0, -2) .'.'. substr($parts[1], -2));
            $vat    = floatval(substr($parts[2], 0, -2) .'.'. substr($parts[2], -2));
        }
    }
    return ...
}
于 2012-10-03T22:05:33.917 に答える