メインコンテンツへ移動

เนื้อหาภาษาไทย / タイ語のコンテンツ

システムガイド 内容確認済み

ทดสอบการชำระเงิน: PayPal

บทความนี้อธิบายวิธีการทดสอบการชำระเงินผ่าน PayPal ซึ่งแตกต่างจาก Stripe ตรงที่ใช้ระบบ Redirect ไปยังหน้า PayPal เพื่อยืนยันการชำระเงิน และต้องจัดการ Callback ต่...

ทดสอบการชำระเงิน: PayPal

ในบทที่แล้ว เราได้เรียนรู้วิธีการทดสอบการชำระเงินผ่าน Stripe โดยใช้ Stripe Elements และ Stripe API ในบทนี้ เราจะมาดูวิธีการทดสอบการชำระเงินผ่าน PayPal โดยใช้คลาส PayPalPayment ที่เราได้สร้างไว้ก่อนหน้านี้ ซึ่งวิธีการจะแตกต่างจาก Stripe พอสมควร เนื่องจาก PayPal มีลักษณะการทำงานแบบ Redirect คือผู้ใช้จะถูกนำไปยังหน้า PayPal เพื่อยืนยันการชำระเงิน และหลังจากนั้น PayPal จะส่งข้อมูลกลับมายังระบบของเรา

สร้าง Form สำหรับ Redirect ไปยัง PayPal

เนื่องจาก PayPal เป็นระบบที่ต้อง Redirect ผู้ใช้ไปยังหน้าของ PayPal เอง เราจึงต้องสร้าง Form ที่จะส่งข้อมูลไปยัง PayPal เพื่อเริ่มต้นกระบวนการชำระเงิน ข้อมูลที่ต้องส่งไปนั้นจะประกอบไปด้วย:

  • business: อีเมล PayPal ของผู้รับเงิน (ผู้ขาย)
  • item_name: ชื่อสินค้าหรือบริการ
  • amount: จำนวนเงิน
  • currency_code: สกุลเงิน
  • return: URL ที่ PayPal จะ Redirect ผู้ใช้กลับมาหลังจากชำระเงินสำเร็จ
  • cancel_return: URL ที่ PayPal จะ Redirect ผู้ใช้กลับมาหากยกเลิกการชำระเงิน
  • notify_url: URL ที่ PayPal จะส่งข้อมูลการชำระเงิน (IPN - Instant Payment Notification)

เหตุผลเชิงออกแบบ: การใช้ Redirect นั้นมีข้อดีคือช่วยลดความซับซ้อนในการจัดการข้อมูลบัตรเครดิต (หรือข้อมูลทางการเงินอื่นๆ) เนื่องจากเราไม่ต้องจัดการข้อมูลเหล่านี้เอง PayPal จะเป็นผู้จัดการให้ทั้งหมด ข้อเสียคือผู้ใช้อาจจะรู้สึกว่าขั้นตอนการชำระเงินยาวนานกว่า และอาจจะเกิดปัญหาในการ Redirect ได้

ตัวอย่าง Form HTML:

<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
  <input type="hidden" name="cmd" value="_xclick">
  <input type="hidden" name="business" value="your_paypal_email@example.com">
  <input type="hidden" name="item_name" value="Example Product">
  <input type="hidden" name="amount" value="10.00">
  <input type="hidden" name="currency_code" value="USD">
  <input type="hidden" name="return" value="http://yourdomain.com/success.php">
  <input type="hidden" name="cancel_return" value="http://yourdomain.com/cancel.php">
  <input type="hidden" name="notify_url" value="http://yourdomain.com/ipn.php">
  <input type="submit" value="Pay with PayPal">
</form>

ข้อควรระวัง: ในสภาพแวดล้อมจริง ควรเปลี่ยน action เป็น https://www.sandbox.paypal.com/cgi-bin/webscr สำหรับการทดสอบใน Sandbox ของ PayPal และใช้บัญชี Sandbox ของ PayPal ในการทดสอบ

จากนั้นใน PHP เราจะสร้างฟังก์ชันเพื่อสร้าง Form นี้โดยอัตโนมัติ:

<?php

function createPayPalForm(array $data): string
{
  $form = '<form action="https://www.paypal.com/cgi-bin/webscr" method="post">';
  foreach ($data as $key => $value) {
    $form .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($value) . '">';
  }
  $form .= '<input type="submit" value="Pay with PayPal"></form>';
  return $form;
}

// ตัวอย่างการใช้งาน
$paypalData = [
  'cmd' => '_xclick',
  'business' => 'your_paypal_email@example.com',
  'item_name' => 'Example Product',
  'amount' => '10.00',
  'currency_code' => 'USD',
  'return' => 'http://yourdomain.com/success.php',
  'cancel_return' => 'http://yourdomain.com/cancel.php',
  'notify_url' => 'http://yourdomain.com/ipn.php',
];

echo createPayPalForm($paypalData);

?>

เมื่อผู้ใช้กดปุ่ม "Pay with PayPal" Form จะถูกส่งไปยัง PayPal และผู้ใช้จะถูก Redirect ไปยังหน้า PayPal เพื่อ Login และยืนยันการชำระเงิน

จัดการ Callback จาก PayPal

หลังจากที่ผู้ใช้ชำระเงิน (หรือยกเลิก) PayPal จะ Redirect ผู้ใช้กลับมายัง URL ที่เรากำหนดไว้ใน return หรือ cancel_return พร้อมกับข้อมูลต่างๆ ในรูปแบบ GET parameters นอกจากนี้ PayPal จะส่งข้อมูลการชำระเงิน (IPN) ไปยัง notify_url ในรูปแบบ POST data

เราต้องสร้างไฟล์ success.php, cancel.php, และ ipn.php เพื่อจัดการกับ Callback เหล่านี้

success.php

ไฟล์นี้จะถูกเรียกเมื่อการชำระเงินสำเร็จ เราสามารถแสดงข้อความขอบคุณและข้อมูลการชำระเงินให้ผู้ใช้ได้

<?php

// ตรวจสอบข้อมูลจาก PayPal (สำคัญมาก)
// ในสภาพแวดล้อมจริง ควรตรวจสอบข้อมูลอย่างละเอียด เช่น transaction ID, amount, currency, status
// และตรวจสอบว่าข้อมูลเหล่านี้ตรงกับข้อมูลที่เราส่งไปตอนแรก

if ($_GET['tx']) {
  // การชำระเงินสำเร็จ
  echo "<h2>Payment Successful!</h2>";
  echo "<p>Transaction ID: " . htmlspecialchars($_GET['tx']) . "</p>";
  // แสดงข้อมูลอื่นๆ ที่จำเป็น
} else {
  // เกิดข้อผิดพลาด
  echo "<h2>Payment Failed!</h2>";
  // แสดงข้อความผิดพลาด
}

?>

cancel.php

ไฟล์นี้จะถูกเรียกเมื่อผู้ใช้ยกเลิกการชำระเงิน เราสามารถแสดงข้อความขออภัยและให้ผู้ใช้ลองใหม่อีกครั้ง

<?php

// ผู้ใช้ยกเลิกการชำระเงิน
echo "<h2>Payment Cancelled!</h2>";
echo "<p>You have cancelled your payment.</p>";
// แสดงข้อความให้ผู้ใช้ลองใหม่อีกครั้ง

?>

ipn.php

ไฟล์นี้จะถูกเรียกโดย PayPal เพื่อส่งข้อมูลการชำระเงิน (IPN) เราต้องตรวจสอบข้อมูลเหล่านี้อย่างละเอียดเพื่อยืนยันว่าการชำระเงินนั้นถูกต้อง และทำการอัปเดตฐานข้อมูลของเรา

สำคัญมาก: การตรวจสอบ IPN เป็นขั้นตอนที่สำคัญที่สุดในการยืนยันการชำระเงิน เราต้องตรวจสอบข้อมูลที่ได้รับจาก PayPal กับข้อมูลที่เราส่งไปตอนแรก และตรวจสอบว่าสถานะการชำระเงินเป็น "Completed" จริงๆ นอกจากนี้ เราควรตรวจสอบว่า transaction ID ไม่ซ้ำกับ transaction ID ที่เราเคยได้รับมาก่อนหน้านี้ เพื่อป้องกันการโจมตีแบบ replay attack

<?php

// อ่านข้อมูลจาก PayPal
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
    $keyval = explode ('=', $keyval);
    if (count($keyval) == 2)
        $myPost[$keyval[0]] = urldecode($keyval[1]);
}
// อ่านข้อมูลจากระบบของเรา (จำเป็นสำหรับการตรวจสอบความถูกต้อง)
$req = 'cmd=_notify-validate';
foreach ($myPost as $key => $value) {
    $value = urlencode($value);
    $req .= "&$key=$value";
}

// ส่งข้อมูลกลับไปยัง PayPal เพื่อตรวจสอบความถูกต้อง
$ch = curl_init('https://www.paypal.com/cgi-bin/webscr'); // หรือ https://www.sandbox.paypal.com/cgi-bin/webscr สำหรับ Sandbox
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

// หากระบบของคุณไม่ได้เปิด OpenSSL ให้ comment บรรทัดด้านบนและ uncomment บรรทัดด้านล่าง
// curl_setopt($ch, CURLOPT_SSLVERSION, 6); // TLSv1.2

$res = curl_exec($ch);
curl_close($ch);

// ตรวจสอบผลลัพธ์จาก PayPal
if (strcmp ($res, "VERIFIED") == 0) {
    // IPN ได้รับการยืนยันว่ามาจาก PayPal จริง
    // ตรวจสอบข้อมูลอื่นๆ เช่น payment_status, txn_id, receiver_email, mc_currency, mc_gross
    // และทำการอัปเดตฐานข้อมูลของเรา

    $payment_status = $_POST['payment_status'];
    $txn_id = $_POST['txn_id'];
    $receiver_email = $_POST['receiver_email'];
    $mc_currency = $_POST['mc_currency'];
    $mc_gross = $_POST['mc_gross'];

    // ตรวจสอบว่า payment_status คือ "Completed"
    if ($payment_status == "Completed") {
        // ตรวจสอบว่า txn_id ไม่ซ้ำกับ txn_id ที่เคยได้รับมาก่อนหน้านี้
        // ตรวจสอบว่า receiver_email ตรงกับอีเมล PayPal ของเรา
        // ตรวจสอบว่า mc_currency และ mc_gross ตรงกับข้อมูลที่เราส่งไปตอนแรก

        // อัปเดตฐานข้อมูลของเรา
        // ...
    }

} else if (strcmp ($res, "INVALID") == 0) {
    // IPN ไม่ถูกต้อง
    // อาจเกิดจากการปลอมแปลงข้อมูล
    // บันทึกข้อมูลเพื่อตรวจสอบต่อไป

}

?>

ข้อควรระวัง: โค้ด IPN ที่ให้มาเป็นเพียงตัวอย่างเท่านั้น ในสภาพแวดล้อมจริง คุณต้องตรวจสอบข้อมูลอย่างละเอียด และทำการ Logging ข้อมูลทั้งหมดเพื่อตรวจสอบในภายหลัง

ตรวจสอบผลลัพธ์

หลังจากที่ผู้ใช้ชำระเงิน (หรือยกเลิก) และระบบของเราได้รับ Callback จาก PayPal เราต้องตรวจสอบผลลัพธ์อย่างละเอียด เพื่อให้แน่ใจว่าการชำระเงินนั้นถูกต้อง และทำการอัปเดตฐานข้อมูลของเราให้ถูกต้อง

แนวปฏิบัติที่ดี:

  • Logging: บันทึกข้อมูลทั้งหมดที่ได้รับจาก PayPal เพื่อตรวจสอบในภายหลัง
  • Idempotency: ตรวจสอบว่า transaction ID ไม่ซ้ำกับ transaction ID ที่เคยได้รับมาก่อนหน้านี้ เพื่อป้องกันการประมวลผลซ้ำ
  • Error Handling: จัดการกับข้อผิดพลาดที่อาจเกิดขึ้น เช่น การเชื่อมต่อกับ PayPal ล้มเหลว หรือข้อมูลที่ได้รับไม่ถูกต้อง
  • Security: ตรวจสอบข้อมูลที่ได้รับจาก PayPal อย่างละเอียด เพื่อป้องกันการโจมตีแบบต่างๆ

ในบทนี้ เราได้เรียนรู้วิธีการทดสอบการชำระเงินผ่าน PayPal โดยใช้คลาส PayPalPayment ที่เราได้สร้างไว้ก่อนหน้านี้ ในบทถัดไป เราจะมาสรุปบทเรียนทั้งหมด และพูดถึงแนวทางการปรับปรุงและขยายระบบของเราต่อไป

Office.in.th Custom Service

この構成をもとにシステムを開発・カスタマイズしたいですか?

Office.in.th は、エンタープライズ Web アプリケーション、API 連携、官民の業務管理システムの設計・開発を承り、長期的なサポートも提供します。

ソフトウェア開発サービスを見る