Laravel 8 向CopyAndPay API发送POST请求并渲染支付表单报错求助
Fixing Your Laravel CopyAndPay Integration Issues
Hey there! Let's break down what's going wrong with your setup and get that payment flow working. I'll walk through the key problems in your code and show you the corrected versions.
Key Issues in Your Current Code
- Your
PaymentControllerhas no actual CURL logic to send the request to the CopyAndPay API - You're hardcoding the
amountinstead of using the value submitted from your form - The payment widget script in your view is using
{entityId}instead of thecheckoutIdreturned by the API (this is critical— the widget needs the unique checkout ID to load correctly) - Your flow doesn't handle passing the API response data back to the view to render the payment form
Corrected Controller Code
namespace App\Http\Controllers; use Illuminate\Http\Request; class PaymentController extends Controller { public function showPaymentForm() { // Show the initial form where users enter amount/reference return view('payment'); } public function processPaymentRequest(Request $request) { // Validate the incoming form data first $validated = $request->validate([ 'amount' => 'required|numeric|min:0.01', 'reference' => 'required|string', ]); $url = "https://test.oppwa.com/v1/checkouts"; $apiParams = [ 'entityId' => '8a8294174b7ecb28014b9699220015ca', 'amount' => number_format($validated['amount'], 2), // Ensure 2 decimal places 'currency' => 'EUR', 'paymentType' => 'DB', 'merchantTransactionId' => $validated['reference'], // Pass your reference to the API ]; // Initialize CURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($apiParams)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Add auth headers if required (check CopyAndPay docs for your setup) // curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_API_KEY']); // Execute request and get response $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // Handle API response if ($httpCode !== 200) { // Handle error (e.g., return back with error message) return back()->withErrors(['api_error' => 'Failed to create checkout session.']); } $responseData = json_decode($response, true); $checkoutId = $responseData['id'] ?? null; if (!$checkoutId) { return back()->withErrors(['api_error' => 'Invalid response from payment API.']); } // Pass checkoutId to the view to render the payment widget return view('payment', compact('checkoutId')); } }
Corrected View Code (resources/views/payment.blade.php)
<!DOCTYPE html> <html> <head> @if(isset($checkoutId)) <!-- Load the payment widget ONLY if we have a valid checkoutId --> <script src="https://test.oppwa.com/v1/paymentWidgets.js?checkoutId={{ $checkoutId }}"></script> @endif </head> <body> <x-app-layout> <x-slot name="header"> <h2 class="font-semibold text-xl text-gray-800 leading-tight"> {{ __('Dashboard') }} </h2> <a href="{{ url('/payment') }}" class="text-sm text-gray-700 underline">Payment</a> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 bg-white border-b border-gray-200"> <!-- Show amount/reference form only if we don't have a checkoutId yet --> @if(!isset($checkoutId)) <form action="{{ route('payment.process') }}" method="POST"> @csrf <div class="myDiv"> <label for="amount">Amount:</label> <input type="text" id="amount" name="amount" value="{{ old('amount') }}"> @error('amount') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror <br><br> <label for="reference">Reference:</label> <input type="text" id="reference" name="reference" value="{{ old('reference') }}"> @error('reference') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror <br><br> <input type="submit" value="Proceed to Payment"> </div> </form> @endif </div> </div> </div> </div> <!-- Show payment widget only if we have a checkoutId --> @if(isset($checkoutId)) <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 bg-white border-b border-gray-200"> <h3 class="mb-4">Complete Your Payment</h3> <form action="{{ route('payment.success') }}" class="paymentWidgets" data-brands="VISA MASTER AMEX"> @csrf </form> </div> </div> </div> </div> @endif </x-app-layout> </body> </html>
Corrected Route Code (routes/web.php)
// Show the initial payment form Route::get('/payment', [PaymentController::class, 'showPaymentForm'])->name('payment.form'); // Process the form submission and fetch checkoutId from API Route::post('/payment/process', [PaymentController::class, 'processPaymentRequest'])->name('payment.process'); // Optional: Route to handle payment success (you'll need to set this up with CopyAndPay webhooks) Route::post('/payment/success', [PaymentController::class, 'handlePaymentSuccess'])->name('payment.success');
What Changed & Why
- Controller Split: I split the logic into two methods—one to show the form, another to process the API request. This makes the flow cleaner.
- Validation: Added form validation to ensure we get valid amount and reference values.
- CURL Implementation: Added full CURL logic to send the POST request to the CopyAndPay API, including handling response codes and parsing the JSON response to get the
checkoutId. - View Logic: The view now conditionally shows either the amount form or the payment widget based on whether we have a
checkoutId. The widget script now uses the dynamiccheckoutIdfrom the controller. - Route Naming: Used named routes for better maintainability and to avoid hardcoding URLs.
Make sure to check the CopyAndPay documentation for any additional headers or parameters you might need (like API authentication if your setup requires it).
内容的提问来源于stack exchange,提问作者deecobbk
相关产品推荐
相关产品推荐

