PSD2 European Compliance

The Payment Services Directive 2 (PSD2) is an EEA regulation that requires changes to the check-out and booking process for all transactions involving a credit card issued by an EEA state. For more information on PSD2 please see here.

1. SHOP

Get availability

List<PropertyAvailability> propertyAvailabilityList = rapidClient.getAvailability(
        "YYYY-MM-DD",
        "YYYY-MM-DD",
        "USD",
        "LANGUAGE",
        "US",
        List.of("OCCUPANCY"),
        List.of("PROPERTY_ID"),
        "SALES-CHANNEL",
        "SALES-ENV",
        BigDecimal.ONE,
        "CUSTOMER-IP"
);

Get price check token

String getToken(String href) throws MalformedURLException {
        URL url = new URL(/* ENDPOINT */ + href);
        String query = url.getQuery();
        String[] split = query.split("&");
        String token = null;
        for (String params : split) {
            String prefix = "token=";
            if (params.startsWith(prefix)) {
                token = params.replaceAll(prefix, "");
            }
        }
        return token;
}

String getPriceCheckToken(List<PropertyAvailability> list) throws MalformedURLException {
        return getToken(list.get(0).getRooms().get(0).getRates().get(0).getBedGroups().entrySet().stream().findFirst().get().getValue().getLinks().getPriceCheck().getHref());
}

Check room prices

RoomPriceCheck roomPriceCheck = rapidClient.priceCheck(
        "11775754",
        propertyAvailabilityList.get(0).getRooms().get(0).getId(),
        propertyAvailabilityList.get(0).getRooms().get(0).getRates().get(0).getId(),
        getPriceCheckToken(propertyAvailabilityList)
);

2. BOOK

Get payment session token and create payment session request

String getPaymentSessionToken(RoomPriceCheck roomPriceCheck) throws MalformedURLException {
        return getToken(roomPriceCheck.getLinks().getPaymentSession().getHref());
}

PaymentSessionsRequestCustomerAccountDetails customerAccountDetails =
        PaymentSessionsRequestCustomerAccountDetails.builder()
                .authenticationMethod(PaymentSessionsRequestCustomerAccountDetails.AuthenticationMethod.GUEST)
                .authenticationTimestamp("2018-02-12T11:59:00.000Z")
                .createDate("2018-09-15")
                .changeDate("2018-09-17")
                .passwordChangeDate("2018-09-17")
                .addCardAttempts(BigDecimal.ONE)
                .accountPurchases(BigDecimal.ONE)
                .build();

BillingContactRequestAddress address = 
        BillingContactRequestAddress.builder()
                .line1("555 1st St")
                .line2("10th Floor")
                .line3("Unit 12")
                .city("Seattle")
                .stateProvinceCode("WA")
                .countryCode(Constants.COUNTRY_CODE)
                .postalCode("98121")
                .build()


BillingContactRequest billingContact =
        BillingContactRequest.builder()
                .givenName("John")
                .familyName("Smith")
                .address(address)
                .build();

List<PaymentRequest> payments = List.of(
        PaymentRequest.builder()
        .type(PaymentRequest.Type.CUSTOMER_CARD)
        .number("4111111111111111")
        .securityCode("123")
        .expirationMonth("08")
        .expirationYear("2025")
        .billingContact(billingContact)
        .enrollmentDate("2018-09-15")
        .build()
)

PaymentSessionsRequest createPaymentSessionRequest() {
        return PaymentSessionsRequest.builder()
                .version("1")
                .browserAcceptHeader("*/*")
                .encodedBrowserMetadata("ZW5jb2RlZF9icm93c2VyX21ldGFkYXRh")
                .preferredChallengeWindowSize(PaymentSessionsRequest.PreferredChallengeWindowSize.MEDIUM)
                .merchantUrl("https://server.adomainname.net")
                .customerAccountDetails(customerAccountDetails)
                .payments(payments)
                .build();
}

Create payment session

PaymentSessions paymentSessions = rapidClient.postPaymentSessions(
        /* CUSTOMER_IP */,
        getPaymentSessionToken(roomPriceCheck),
        createPaymentSessionRequest()
);

Pass the JavaScript challenge

If 2FA is required, the response will include an encodedChallengeConfig. The encodedChallengeConfig and paymentSessionId returned will need to be passed as parameters into the JavaScript challenge method.

Get post itinerary token and create itinerary request

String getPostItineraryToken(RoomPriceCheck priceCheck) throws MalformedURLException {
        return getToken(priceCheck.getLinks().getBook().getHref());
}

PhoneRequest phone =
        PhoneRequest.builder()
                .countryCode("1")
                .areaCode("487")
                .number("5550077")
                .build();

List<CreateItineraryRequestRoom> rooms = List.of(
        CreateItineraryRequestRoom.builder()
                .givenName("John")
                .familyName("Smith")
                .smoking(false)
                .specialRequest("Top floor or away from street please")
                .build()
);

BillingContactRequestAddress address =
        BillingContactRequestAddress.builder()
                .line1("555 1st St")
                .line2("10th Floor")
                .line3("Unit 12")
                .city("Seattle")
                .stateProvinceCode("WA")
                .countryCode(/* COUNTRY_CODE */)
                .postalCode("98121")
                .build();


BillingContactRequest billingContact = 
        BillingContactRequest.builder()
                .givenName("John")
                .familyName("Smith")
                .address(address)
                .build();

List<PaymentRequest> payments = List.of(
        PaymentRequest.builder()
                .type(PaymentRequest.Type.CUSTOMER_CARD)
                .number("4111111111111111")
                .securityCode("123")
                .expirationMonth("08")
                .expirationYear("2025")
                .billingContact(billingContact)
                .enrollmentDate("2018-09-15")
                .build();
)


CreateItineraryRequest createItineraryRequest(boolean hold) {
        return CreateItineraryRequest.builder()
                .affiliateReferenceId(UUID.randomUUID().toString().substring(0, 28))
                .hold(hold)
                .email("john@example.com")
                .phone(phone)
                .rooms(rooms)
                .payments(payments)
                .affiliateMetadata("data_point_1:123|data_point2:This is data.")
                .taxRegistrationNumber("12345678910")
                .travelerHandlingInstructions("Please use the card provided for payment. Avoid cancelation as this is for a corporate traveler. Contact traveler if any issues.")
                .build();
    }

Create itinerary

ItineraryCreation itineraryCreation = rapidClient.postItinerary(
        /* CUSTOMER_IP */,
        getPostItineraryToken(roomPriceCheck),
        createItineraryRequest(true)
);

Get complete payment session token

String getCompletePaymentSessionToken(ItineraryCreation itineraryCreation) throws MalformedURLException {
        return getToken(itineraryCreation.getLinks().getCompletePaymentSession().getHref());
    }

Complete payment session

CompletePaymentSession completePaymentSession = rapidClient.putCompletePaymentSession(
        /* CUSTOMER_IP */,
        itineraryCreation.getItineraryId(),
        getCompletePaymentSessionToken(itineraryCreation)
);

Get resume booking token

String getResumeBookingToken(ItineraryCreation itineraryCreation) throws MalformedURLException {
        return getToken(itineraryCreation.getLinks().getResume().getHref());
}

Resume on-hold booking

rapidClient.putResumeBooking(
        /* CUSTOMER_IP */,
        completePaymentSession.getItineraryId(),
        getResumeBookingToken(itineraryCreation)
);
Did you find this page helpful?
How can we improve this content?
Thank you for helping us improve Developer Hub!