Plants
& More

Multibanco Payment Method

This example demonstrates a payment form using the Multibanco payment method, a popular bank payment network in Portugal. After initiating the charge, the customer receives an entity number and reference number to complete the payment at an ATM or online banking portal.

Payment expiry

You can optionally set an expiry deadline for the payment by passing multibanco.expiresAt when creating the charge. This field accepts a Unix timestamp (seconds since epoch) representing the moment the payment deadline expires. It must be at least 1 minute and at most 7 days in the future.

In test mode, the expiry is capped at 15 minutes regardless of the value provided. Once the deadline passes, the charge is automatically failed. If no expiresAt is provided, a default expiry of 7 days is applied automatically.

Expiry simulation in test mode

This example sets expiresAt based on the billing name you provide. Use the following naming convention to trigger a specific expiry duration:

  • Expire after 1 — expires in 1 minute
  • Expire after 3 — expires in 3 minutes
  • Expire after 5 — expires in 5 minutes
  • Expire after 10 — expires in 10 minutes

Any other billing name defaults to a 60-minute expiry. In test mode the backend caps the simulator delay at 15 minutes, so a charge with a longer expiry auto-fails after 15 minutes.

Payment flow technical overview

The full transaction involves a few straightforward steps:

  • The backend creates a Multibanco Payment Method and then a Charge using the returned Payment Method ID.
  • The Charge initially has a pending status and flow.nextAction equals redirect.
  • The customer completes the payment using the provided entity number and reference number at an ATM or through online banking.
  • The Charge status is updated to successful or failed depending on the payment outcome.

Client object ID

Every Charge includes a clientObjectId, which can be used with shift4.js. It allows retrieving all necessary payment information in the browser without exposing sensitive data.

Simplified integration with shift4.js

By using shift4.js, handling the Multibanco flow becomes easier. It can manage redirects and automatically track the final payment status for you.

To use this functionality, pass the clientObjectId from your server to the browser and invoke the handleChargeNextAction method with it.

You'll need to access clientObjectId in two situations:

  • Right after the Charge is created on your backend – return it to the frontend.
  • When the customer is redirected back to your website – it will be part of the return URL's query parameters.

Showing final result

Once the payment is completed, handleChargeNextAction will resolve with a minimal Charge object, exposing only the required result information, ensuring user privacy and security.


@RestController
@RequestMapping("/ajax/examples/charge-with-multibanco")
@RequiredArgsConstructor
class ExamplesAjaxChargeWithMultibancoController {

    @PostMapping("/payment")
    Map<String, String> ajaxPaymentMethod(@RequestBody PaymentMethodExampleRequest exampleRequest) throws IOException {
        try (Shift4Gateway shift4Gateway = createShift4GatewayForPaymentMethods()) {

            long expiresAt = Instant.now().plusSeconds(60 * 60L).getEpochSecond(); // 1 hour from now
            var paymentMethodRequest = new PaymentMethodRequest()
                    .set("type", "multibanco")
                    .set("multibanco", Map.of("expiresAt", expiresAt))
                    .billing(new BillingRequest()
                            .name(exampleRequest.name)
                            .address(new AddressRequest().country("PT")));
            var paymentMethod = shift4Gateway.createPaymentMethod(paymentMethodRequest);

            var chargeRequest = new ChargeRequest(100, "EUR")
                    .paymentMethod(new PaymentMethodRequest(paymentMethod.getId()))
                    .flow(new ChargeFlowRequest()
                            .returnUrl("https://your-website.com/examples/charge-with-multibanco"));
            var charge = shift4Gateway.createCharge(chargeRequest);

            return singletonMap("clientObjectId", charge.getClientObjectId());

        } catch (Shift4Exception e) {
            throw new BadRequestException(e.getMessage());
        }
    }

    private static int resolveExpiryMinutes(String billingName) {
        if (billingName != null) {
            Matcher matcher = Pattern.compile("(?i)expire after (\\d+)").matcher(billingName);
            if (matcher.find()) {
                return Integer.parseInt(matcher.group(1));
            }
        }
        return 60;
    }

    static class PaymentMethodExampleRequest {
        String name;
    }
}