[ 'method' => 'POST', 'header' => "Content-Type: application/json\r\n" . 'callbackIdentifier: ' . $payload['identifier'] . "\r\n", 'content' => $body, 'timeout' => 15, 'ignore_errors' => true, ], ]); $result = @file_get_contents($payload['url'], false, $context); file_put_contents('/tmp/fake-swish.log', sprintf( "[%s] callback %s -> %s\n", date('H:i:s'), $payload['url'], $result === false ? 'FAILED' : 'sent' ), FILE_APPEND); exit(0); } // First mode: the payment request itself $request = json_decode(file_get_contents('php://input'), true) ?: []; $amount = (float) ($request['amount'] ?? 0); file_put_contents('/tmp/fake-swish.log', sprintf( "[%s] %s %s %s\n", date('H:i:s'), $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], json_encode($request) ), FILE_APPEND); // Swish rejects a payment request without a payee, and so do we if (empty($request['payeeAlias'])) { http_response_code(422); header('Content-Type: application/json'); echo json_encode([['errorCode' => 'PA02', 'errorMessage' => 'Payee alias is missing']]); return; } $instructionUuid = basename($_SERVER['REQUEST_URI']); header('PaymentRequestToken: FAKE' . substr($instructionUuid, 0, 12)); header('Location: http://127.0.0.1:9099/swish-cpcapi/api/v1/paymentrequests/' . $instructionUuid); http_response_code(201); // 1 kr means "the payer never finishes", so no callback is ever sent if ((int) $amount === 1) { return; } $callback = [ // Already ends in /kiosk — the app puts it there so the receiver knows where to forward 'url' => $request['callbackUrl'], 'identifier' => $request['callbackIdentifier'] ?? '', 'body' => [ 'id' => $instructionUuid, 'payeePaymentReference' => $request['payeePaymentReference'] ?? '', 'paymentReference' => strtoupper(bin2hex(random_bytes(16))), 'payerAlias' => '46701234567', 'payeeAlias' => $request['payeeAlias'], 'amount' => $amount, 'currency' => 'SEK', 'message' => $request['message'] ?? '', 'status' => (int) $amount === 13 ? 'DECLINED' : 'PAID', 'errorCode' => (int) $amount === 13 ? 'BANKIDCL' : null, 'errorMessage' => '', 'dateCreated' => date('c'), 'datePaid' => (int) $amount === 13 ? null : date('c'), ], ]; // Answer first, deliver the callback afterwards — the same order as the real thing exec(sprintf( 'php %s %s > /dev/null 2>&1 &', escapeshellarg(__FILE__), escapeshellarg(base64_encode(json_encode($callback))) ));