Repository: kiwicom/mobile
Pull request #1333 (2018-11-14 15:15:49)

Hotels: Prepare payment screen for stay22 integration
Note that we now get to the webview with stay22, but the room is not selected because they provide us with wrong id's, I have asked them to correct this. 


Patch:
diff --git a/app/hotels/src/navigation/HotelsNavigationStack.js b/app/hotels/src/navigation/HotelsNavigationStack.js
index 9ee039a0..c00cbb8d 100644
--- a/app/hotels/src/navigation/HotelsNavigationStack.js
+++ b/app/hotels/src/navigation/HotelsNavigationStack.js
@@ -13,7 +13,7 @@ import SingleHotel from './singleHotel/SingleHotelNavigationScreen';
 import SearchResults from './allHotels/SearchResultsScreen';
 import AdditionalPropsInjector from './singleHotel/AdditionalPropsInjector';
 import SingleHotelMapNavigationScreen from './singleHotel/SingleHotelMapNavigationScreen';
-import Payment from '../singleHotel/PaymentScreen';
+import Payment from '../singleHotel/paymentScreen/PaymentScreen';
 import GalleryGrid from '../gallery/GalleryGrid';
 
 export type NavigationProps = {|
diff --git a/app/hotels/src/navigation/HotelsTabletStack.js b/app/hotels/src/navigation/HotelsTabletStack.js
index a6ed5700..15776ea1 100644
--- a/app/hotels/src/navigation/HotelsTabletStack.js
+++ b/app/hotels/src/navigation/HotelsTabletStack.js
@@ -12,7 +12,7 @@ import { withMappedNavigationAndConfigProps as withMappedProps } from 'react-nav
 import SearchResults from './allHotels/SearchResultsScreen';
 import AdditionalPropsInjector from './singleHotel/AdditionalPropsInjector';
 import SingleHotelMapNavigationScreen from './singleHotel/SingleHotelMapNavigationScreen';
-import Payment from '../singleHotel/PaymentScreen';
+import Payment from '../singleHotel/paymentScreen/PaymentScreen';
 import GalleryGrid from '../gallery/GalleryGrid';
 
 export type NavigationProps = {|
diff --git a/app/hotels/src/navigation/singleHotel/SingleHotelStack.js b/app/hotels/src/navigation/singleHotel/SingleHotelStack.js
index cbe16f7e..19eb13b0 100644
--- a/app/hotels/src/navigation/singleHotel/SingleHotelStack.js
+++ b/app/hotels/src/navigation/singleHotel/SingleHotelStack.js
@@ -11,7 +11,7 @@ import {
 import SingleHotelNavigationScreen from './SingleHotelNavigationScreen';
 import SingleHotelMapNavigationScreen from './SingleHotelMapNavigationScreen';
 import GalleryGrid from '../../gallery/GalleryGrid';
-import PaymentScreen from '../../singleHotel/PaymentScreen';
+import PaymentScreen from '../../singleHotel/paymentScreen/PaymentScreen';
 import AdditionalPropsInjector from './AdditionalPropsInjector';
 
 const DetailStack = StackNavigator(
diff --git a/app/hotels/src/singleHotel/PaymentScreen.js b/app/hotels/src/singleHotel/PaymentScreen.js
deleted file mode 100644
index 99887cac..00000000
--- a/app/hotels/src/singleHotel/PaymentScreen.js
+++ /dev/null
@@ -1,152 +0,0 @@
-// @flow
-
-import * as React from 'react';
-import { Platform, StatusBar, View } from 'react-native';
-import {
-  WebView,
-  Logger,
-  type WebViewStateChangeEvent,
-  GeneralError,
-} from '@kiwicom/mobile-shared';
-import { DateUtils, Translation } from '@kiwicom/mobile-localization';
-import querystring from 'querystring';
-import { HeaderTitle, type NavigationType } from '@kiwicom/mobile-navigation';
-import { graphql, PublicApiRenderer } from '@kiwicom/mobile-relay';
-import { HeaderBackButton } from 'react-navigation';
-import { defaultTokens } from '@kiwicom/mobile-orbit';
-
-import { sanitizeDate } from '../GraphQLSanitizers';
-import { withHotelsContext } from '../HotelsContext';
-import type { PaymentScreenQueryResponse } from './__generated__/PaymentScreenQuery.graphql';
-
-export type PaymentParameters = {|
-  +hotelId: string,
-  +checkin: Date,
-  +checkout: Date,
-  +rooms: Array<{|
-    +id: string,
-    +count: number, // how many rooms with this ID?
-  |}>,
-  +language: string,
-  +currency: string,
-  +version: string,
-|};
-
-type RoomConfig = {|
-  +roomId: string,
-  +count: number,
-|};
-
-export class PaymentScreen extends React.Component<PaymentParameters> {
-  static navigationOptions = ({
-    navigation,
-  }: {
-    navigation: NavigationType,
-  }) => {
-    function goBack() {
-      navigation.goBack(null);
-    }
-    return {
-      headerTitle: (
-        <HeaderTitle>
-          <Translation id="hotels.navigation.title.payment" />
-        </HeaderTitle>
-      ),
-      headerLeft: (
-        <HeaderBackButton
-          onPress={goBack}
-          tintColor={defaultTokens.paletteProductNormal}
-        />
-      ),
-    };
-  };
-
-  componentDidMount() {
-    Logger.ancillaryDisplayed(
-      Logger.Type.ANCILLARY_STEP_PAYMENT,
-      Logger.Provider.ANCILLARY_PROVIDER_BOOKINGCOM,
-    );
-  }
-
-  onNavigationStateChange = (event: WebViewStateChangeEvent) => {
-    if (!event.loading && event.url.includes('booking.com/confirmation')) {
-      Logger.ancillaryPurchased(
-        Logger.Type.ANCILLARY_STEP_PAYMENT,
-        Logger.Provider.ANCILLARY_PROVIDER_BOOKINGCOM,
-      );
-    }
-  };
-
-  getRoomConfig = () =>
-    this.props.rooms.map<RoomConfig>(room => ({
-      roomId: room.id,
-      count: room.count,
-    }));
-
-  renderInner = (props: PaymentScreenQueryResponse) => {
-    const url = props.hotelPaymentUrls?.bookingComPaymentUrl;
-    if (url == null) {
-      return (
-        <GeneralError
-          errorMessage={<Translation id="hotels.payment_screen.server_error" />}
-        />
-      );
-    }
-    return (
-      <React.Fragment>
-        <View testID="paymentScreenSingleHotel" />
-        <WebView
-          source={{
-            uri: createURI(this.props, url),
-          }}
-          onNavigationStateChange={this.onNavigationStateChange}
-        />
-      </React.Fragment>
-    );
-  };
-
-  render() {
-    return (
-      <React.Fragment>
-        <StatusBar barStyle="dark-content" />
-        <PublicApiRenderer
-          render={this.renderInner}
-          query={graphql`
-            query PaymentScreenQuery(
-              $hotelId: ID
-              $roomConfig: [RoomConfigInput]
-            ) {
-              hotelPaymentUrls(hotelId: $hotelId, roomConfig: $roomConfig) {
-                bookingComPaymentUrl
-              }
-            }
-          `}
-          variables={{
-            hotelId: this.props.hotelId,
-            roomConfig: this.getRoomConfig(),
-          }}
-        />
-      </React.Fragment>
-    );
-  }
-}
-
-export default withHotelsContext(state => ({
-  checkin: state.checkin,
-  checkout: state.checkout,
-  currency: state.currency,
-  version: state.version,
-}))(PaymentScreen);
-
-export function createURI(pp: PaymentParameters, url: string): string {
-  const checkinQuery = sanitizeDate(pp.checkin);
-  const intervalQuery = DateUtils.diffInDays(pp.checkout, pp.checkin);
-
-  return `${url}&${querystring.stringify({
-    checkin: checkinQuery,
-    interval: intervalQuery,
-    label: `kiwi-${Platform.OS}-react-${pp.version}`,
-    lang: pp.language,
-    selected_currency: pp.currency,
-  })}`;
-}
diff --git a/app/hotels/src/singleHotel/paymentScreen/BookingPaymentScreen.js b/app/hotels/src/singleHotel/paymentScreen/BookingPaymentScreen.js
new file mode 100644
index 00000000..a290f29a
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/BookingPaymentScreen.js
@@ -0,0 +1,93 @@
+// @flow strict
+
+import * as React from 'react';
+import { Platform, StatusBar } from 'react-native';
+import { Logger } from '@kiwicom/mobile-shared';
+import { DateUtils, DeviceInfo } from '@kiwicom/mobile-localization';
+import querystring from 'querystring';
+import { graphql, PublicApiRenderer } from '@kiwicom/mobile-relay';
+
+import { sanitizeDate } from '../../GraphQLSanitizers';
+import { withHotelsContext } from '../../HotelsContext';
+import type { BookingPaymentScreenQueryResponse } from './__generated__/BookingPaymentScreenQuery.graphql';
+import PaymentWebView from './PaymentWebView';
+
+type Props = {|
+  +hotelId: string,
+  +checkin: Date,
+  +checkout: Date,
+  +currency: string,
+  +version: string,
+  +roomConfig: $ReadOnlyArray<{|
+    +roomId: string,
+    +count: number,
+  |}>,
+|};
+
+export class BookingPaymentScreen extends React.Component<Props> {
+  componentDidMount() {
+    Logger.ancillaryDisplayed(
+      Logger.Type.ANCILLARY_STEP_PAYMENT,
+      Logger.Provider.ANCILLARY_PROVIDER_BOOKINGCOM,
+    );
+  }
+
+  createURI = (url: ?string): ?string => {
+    if (url == null) {
+      return null;
+    }
+
+    const checkinQuery = sanitizeDate(this.props.checkin);
+    const intervalQuery = DateUtils.diffInDays(
+      this.props.checkout,
+      this.props.checkin,
+    );
+
+    return `${url}&${querystring.stringify({
+      checkin: checkinQuery,
+      interval: intervalQuery,
+      label: `kiwi-${Platform.OS}-react-${this.props.version}`,
+      lang: DeviceInfo.getLanguage(),
+      selected_currency: this.props.currency,
+    })}`;
+  };
+
+  renderInner = (props: BookingPaymentScreenQueryResponse) => (
+    <PaymentWebView
+      url={this.createURI(props.hotelPaymentUrls?.bookingComPaymentUrl)}
+    />
+  );
+
+  render() {
+    return (
+      <React.Fragment>
+        <StatusBar barStyle="dark-content" />
+        <PublicApiRenderer
+          render={this.renderInner}
+          query={graphql`
+            query BookingPaymentScreenQuery(
+              $hotelId: ID
+              $roomConfig: [RoomConfigInput]
+            ) {
+              hotelPaymentUrls(hotelId: $hotelId, roomConfig: $roomConfig) {
+                bookingComPaymentUrl
+              }
+            }
+          `}
+          variables={{
+            hotelId: this.props.hotelId,
+            roomConfig: this.props.roomConfig,
+          }}
+        />
+      </React.Fragment>
+    );
+  }
+}
+
+export default withHotelsContext(state => ({
+  checkin: state.checkin,
+  checkout: state.checkout,
+  currency: state.currency,
+  version: state.version,
+  hotelId: state.hotelId,
+}))(BookingPaymentScreen);
diff --git a/app/hotels/src/singleHotel/paymentScreen/PaymentScreen.js b/app/hotels/src/singleHotel/paymentScreen/PaymentScreen.js
new file mode 100644
index 00000000..e3c8f85c
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/PaymentScreen.js
@@ -0,0 +1,71 @@
+// @flow
+
+import * as React from 'react';
+import { Translation } from '@kiwicom/mobile-localization';
+import { HeaderTitle, type NavigationType } from '@kiwicom/mobile-navigation';
+import { HeaderBackButton } from 'react-navigation';
+import { defaultTokens } from '@kiwicom/mobile-orbit';
+
+import {
+  withHotelsContext,
+  type HotelsContextState,
+  type ApiProvider,
+} from '../../HotelsContext';
+import BookingPaymentScreen from './BookingPaymentScreen';
+import Stay22PaymentScreen from './Stay22PaymentScreen';
+
+export type Props = {|
+  +apiProvider: ApiProvider,
+  +rooms: Array<{|
+    +id: string,
+    +count: number, // how many rooms with this ID?
+  |}>,
+|};
+
+type RoomConfig = {|
+  +roomId: string,
+  +count: number,
+|};
+
+export class PaymentScreen extends React.Component<Props> {
+  static navigationOptions = ({
+    navigation,
+  }: {
+    navigation: NavigationType,
+  }) => {
+    function goBack() {
+      navigation.goBack(null);
+    }
+    return {
+      headerTitle: (
+        <HeaderTitle>
+          <Translation id="hotels.navigation.title.payment" />
+        </HeaderTitle>
+      ),
+      headerLeft: (
+        <HeaderBackButton
+          onPress={goBack}
+          tintColor={defaultTokens.paletteProductNormal}
+        />
+      ),
+    };
+  };
+
+  getRoomConfig = () =>
+    this.props.rooms.map<RoomConfig>(room => ({
+      roomId: room.id,
+      count: room.count,
+    }));
+
+  render() {
+    const roomConfig = this.getRoomConfig();
+    if (this.props.apiProvider === 'booking') {
+      return <BookingPaymentScreen roomConfig={roomConfig} />;
+    }
+    return <Stay22PaymentScreen roomConfig={roomConfig} />;
+  }
+}
+
+export default withHotelsContext((state: HotelsContextState) => ({
+  apiProvider: state.apiProvider,
+}))(PaymentScreen);
diff --git a/app/hotels/src/singleHotel/paymentScreen/PaymentWebView.js b/app/hotels/src/singleHotel/paymentScreen/PaymentWebView.js
new file mode 100644
index 00000000..605ec0a5
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/PaymentWebView.js
@@ -0,0 +1,69 @@
+// @flow strict
+
+import * as React from 'react';
+import { View } from 'react-native';
+import {
+  WebView,
+  GeneralError,
+  type WebViewStateChangeEvent,
+  Logger,
+} from '@kiwicom/mobile-shared';
+import { Translation } from '@kiwicom/mobile-localization';
+
+import {
+  withHotelsContext,
+  type HotelsContextState,
+  type ApiProvider,
+} from '../../HotelsContext';
+
+type Props = {|
+  +url: ?string,
+  +apiProvider: ApiProvider,
+|};
+
+export class PaymentWebView extends React.Component<Props> {
+  onNavigationStateChange = (event: WebViewStateChangeEvent) => {
+    /**
+     * Still we are only being redirected to booking.com for payment.
+     * If that will change in the future, we might pass this url as a prop from parent component
+     */
+    if (!event.loading && event.url.includes('booking.com/confirmation')) {
+      const {
+        ANCILLARY_PROVIDER_BOOKINGCOM,
+        ANCILLARY_PROVIDER_STAY22,
+      } = Logger.Provider;
+
+      Logger.ancillaryPurchased(
+        Logger.Type.ANCILLARY_STEP_PAYMENT,
+        this.props.apiProvider === 'booking'
+          ? ANCILLARY_PROVIDER_BOOKINGCOM
+          : ANCILLARY_PROVIDER_STAY22,
+      );
+    }
+  };
+
+  render() {
+    if (this.props.url == null) {
+      return (
+        <GeneralError
+          errorMessage={<Translation id="hotels.payment_screen.server_error" />}
+        />
+      );
+    }
+    return (
+      <React.Fragment>
+        <View testID="paymentScreenSingleHotel" />
+        <WebView
+          source={{
+            uri: this.props.url,
+          }}
+          onNavigationStateChange={this.onNavigationStateChange}
+        />
+      </React.Fragment>
+    );
+  }
+}
+
+const select = ({ apiProvider }: HotelsContextState) => ({ apiProvider });
+
+export default withHotelsContext(select)(PaymentWebView);
diff --git a/app/hotels/src/singleHotel/paymentScreen/Stay22PaymentScreen.js b/app/hotels/src/singleHotel/paymentScreen/Stay22PaymentScreen.js
new file mode 100644
index 00000000..175cd094
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/Stay22PaymentScreen.js
@@ -0,0 +1,55 @@
+// @flow strict
+
+import * as React from 'react';
+import { Logger } from '@kiwicom/mobile-shared';
+import { graphql, PublicApiRenderer } from '@kiwicom/mobile-relay';
+
+import { withHotelsContext } from '../../HotelsContext';
+import PaymentWebView from './PaymentWebView';
+import type { Stay22PaymentScreenQueryResponse } from './__generated__/Stay22PaymentScreenQuery.graphql';
+
+type Props = {|
+  +paymentLink: ?string,
+  +roomConfig: $ReadOnlyArray<{|
+    +roomId: string,
+    +count: number,
+  |}>,
+|};
+
+class Stay22PaymentScreen extends React.Component<Props> {
+  componentDidMount() {
+    Logger.ancillaryDisplayed(
+      Logger.Type.ANCILLARY_STEP_PAYMENT,
+      Logger.Provider.ANCILLARY_PROVIDER_STAY22,
+    );
+  }
+
+  renderInner = (props: Stay22PaymentScreenQueryResponse) => (
+    <PaymentWebView url={props.hotelPaymentUrls?.stay22PaymentUrl} />
+  );
+
+  render() {
+    return (
+      <PublicApiRenderer
+        render={this.renderInner}
+        query={graphql`
+          query Stay22PaymentScreenQuery(
+            $paymentLink: String!
+            $roomConfig: [RoomConfigInput]
+          ) {
+            hotelPaymentUrls(roomConfig: $roomConfig) {
+              stay22PaymentUrl(paymentLink: $paymentLink)
+            }
+          }
+        `}
+        variables={{
+          paymentLink: this.props.paymentLink,
+          roomConfig: this.props.roomConfig,
+        }}
+      />
+    );
+  }
+}
+export default withHotelsContext(state => ({
+  paymentLink: state.paymentLink,
+}))(Stay22PaymentScreen);
diff --git a/app/hotels/src/singleHotel/__generated__/PaymentScreenQuery.graphql.js b/app/hotels/src/singleHotel/paymentScreen/__generated__/BookingPaymentScreenQuery.graphql.js
similarity index 72%
rename from app/hotels/src/singleHotel/__generated__/PaymentScreenQuery.graphql.js
rename to app/hotels/src/singleHotel/paymentScreen/__generated__/BookingPaymentScreenQuery.graphql.js
index 52673059..0de3d74c 100644
--- a/app/hotels/src/singleHotel/__generated__/PaymentScreenQuery.graphql.js
+++ b/app/hotels/src/singleHotel/paymentScreen/__generated__/BookingPaymentScreenQuery.graphql.js
@@ -1,6 +1,6 @@
 /**
  * @flow
- * @relayHash 28a12649218f3e7a941c619921aa507b
+ * @relayHash 5d92fb044f70f4e64e8d7f64d9402ada
  */
 
 /* eslint-disable */
@@ -13,24 +13,24 @@ export type RoomConfigInput = {
   roomId: string,
   count: number,
 };
-export type PaymentScreenQueryVariables = {|
+export type BookingPaymentScreenQueryVariables = {|
   hotelId?: ?string,
   roomConfig?: ?$ReadOnlyArray<?RoomConfigInput>,
 |};
-export type PaymentScreenQueryResponse = {|
+export type BookingPaymentScreenQueryResponse = {|
   +hotelPaymentUrls: ?{|
     +bookingComPaymentUrl: ?string
   |}
 |};
-export type PaymentScreenQuery = {|
-  variables: PaymentScreenQueryVariables,
-  response: PaymentScreenQueryResponse,
+export type BookingPaymentScreenQuery = {|
+  variables: BookingPaymentScreenQueryVariables,
+  response: BookingPaymentScreenQueryResponse,
 |};
 */
 
 
 /*
-query PaymentScreenQuery(
+query BookingPaymentScreenQuery(
   $hotelId: ID
   $roomConfig: [RoomConfigInput]
 ) {
@@ -91,13 +91,13 @@ v1 = [
 return {
   "kind": "Request",
   "operationKind": "query",
-  "name": "PaymentScreenQuery",
+  "name": "BookingPaymentScreenQuery",
   "id": null,
-  "text": "query PaymentScreenQuery(\n  $hotelId: ID\n  $roomConfig: [RoomConfigInput]\n) {\n  hotelPaymentUrls(hotelId: $hotelId, roomConfig: $roomConfig) {\n    bookingComPaymentUrl\n  }\n}\n",
+  "text": "query BookingPaymentScreenQuery(\n  $hotelId: ID\n  $roomConfig: [RoomConfigInput]\n) {\n  hotelPaymentUrls(hotelId: $hotelId, roomConfig: $roomConfig) {\n    bookingComPaymentUrl\n  }\n}\n",
   "metadata": {},
   "fragment": {
     "kind": "Fragment",
-    "name": "PaymentScreenQuery",
+    "name": "BookingPaymentScreenQuery",
     "type": "RootQuery",
     "metadata": null,
     "argumentDefinitions": v0,
@@ -105,12 +105,12 @@ return {
   },
   "operation": {
     "kind": "Operation",
-    "name": "PaymentScreenQuery",
+    "name": "BookingPaymentScreenQuery",
     "argumentDefinitions": v0,
     "selections": v1
   }
 };
 })();
 // prettier-ignore
-(node/*: any*/).hash = '4c45a0c05b4bc8d0dcc000450b0382a1';
+(node/*: any*/).hash = '2237324bd3f3dab3ca90d1a90e28e5d1';
 module.exports = node;
diff --git a/app/hotels/src/singleHotel/paymentScreen/__generated__/Stay22PaymentScreenQuery.graphql.js b/app/hotels/src/singleHotel/paymentScreen/__generated__/Stay22PaymentScreenQuery.graphql.js
new file mode 100644
index 00000000..95c30c7e
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/__generated__/Stay22PaymentScreenQuery.graphql.js
@@ -0,0 +1,117 @@
+/**
+ * @flow
+ * @relayHash 12cdddc336c8166c26e51d5e7296a907
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { ConcreteRequest } from 'relay-runtime';
+export type RoomConfigInput = {
+  roomId: string,
+  count: number,
+};
+export type Stay22PaymentScreenQueryVariables = {|
+  paymentLink: string,
+  roomConfig?: ?$ReadOnlyArray<?RoomConfigInput>,
+|};
+export type Stay22PaymentScreenQueryResponse = {|
+  +hotelPaymentUrls: ?{|
+    +stay22PaymentUrl: ?string
+  |}
+|};
+export type Stay22PaymentScreenQuery = {|
+  variables: Stay22PaymentScreenQueryVariables,
+  response: Stay22PaymentScreenQueryResponse,
+|};
+*/
+
+
+/*
+query Stay22PaymentScreenQuery(
+  $paymentLink: String!
+  $roomConfig: [RoomConfigInput]
+) {
+  hotelPaymentUrls(roomConfig: $roomConfig) {
+    stay22PaymentUrl(paymentLink: $paymentLink)
+  }
+}
+*/
+
+const node/*: ConcreteRequest*/ = (function(){
+var v0 = [
+  {
+    "kind": "LocalArgument",
+    "name": "paymentLink",
+    "type": "String!",
+    "defaultValue": null
+  },
+  {
+    "kind": "LocalArgument",
+    "name": "roomConfig",
+    "type": "[RoomConfigInput]",
+    "defaultValue": null
+  }
+],
+v1 = [
+  {
+    "kind": "LinkedField",
+    "alias": null,
+    "name": "hotelPaymentUrls",
+    "storageKey": null,
+    "args": [
+      {
+        "kind": "Variable",
+        "name": "roomConfig",
+        "variableName": "roomConfig",
+        "type": "[RoomConfigInput]"
+      }
+    ],
+    "concreteType": "HotelPaymentUrls",
+    "plural": false,
+    "selections": [
+      {
+        "kind": "ScalarField",
+        "alias": null,
+        "name": "stay22PaymentUrl",
+        "args": [
+          {
+            "kind": "Variable",
+            "name": "paymentLink",
+            "variableName": "paymentLink",
+            "type": "String!"
+          }
+        ],
+        "storageKey": null
+      }
+    ]
+  }
+];
+return {
+  "kind": "Request",
+  "operationKind": "query",
+  "name": "Stay22PaymentScreenQuery",
+  "id": null,
+  "text": "query Stay22PaymentScreenQuery(\n  $paymentLink: String!\n  $roomConfig: [RoomConfigInput]\n) {\n  hotelPaymentUrls(roomConfig: $roomConfig) {\n    stay22PaymentUrl(paymentLink: $paymentLink)\n  }\n}\n",
+  "metadata": {},
+  "fragment": {
+    "kind": "Fragment",
+    "name": "Stay22PaymentScreenQuery",
+    "type": "RootQuery",
+    "metadata": null,
+    "argumentDefinitions": v0,
+    "selections": v1
+  },
+  "operation": {
+    "kind": "Operation",
+    "name": "Stay22PaymentScreenQuery",
+    "argumentDefinitions": v0,
+    "selections": v1
+  }
+};
+})();
+// prettier-ignore
+(node/*: any*/).hash = '2e7ce872382d2baed38d7bb70ba521cb';
+module.exports = node;
diff --git a/schema.graphql b/schema.graphql
index 95698578..ff489774 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -1606,6 +1606,7 @@ interface HotelInterface {
 """The urls where the user will be redirected for payment"""
 type HotelPaymentUrls {
   bookingComPaymentUrl: String
+  stay22PaymentUrl(paymentLink: String!): String
 }
 
 type HotelPhoto {


Test patch:
diff --git a/app/hotels/src/singleHotel/__tests__/PaymentScreen.test.js b/app/hotels/src/singleHotel/__tests__/PaymentScreen.test.js
deleted file mode 100644
index 425da2a6..00000000
--- a/app/hotels/src/singleHotel/__tests__/PaymentScreen.test.js
+++ /dev/null
@@ -1,85 +0,0 @@
-// @flow
-
-import { NativeModules } from 'react-native';
-
-import { PaymentScreen, createURI } from '../PaymentScreen';
-
-const ONE_DAY = 8.64e7; // ms
-
-jest.mock('../../../package.json', () => ({
-  version: '1.0.0',
-}));
-
-it('creates correct URL', () => {
-  expect(
-    createURI(
-      {
-        hotelId: '1',
-        checkin: new Date(ONE_DAY),
-        checkout: new Date(3 * ONE_DAY), // 2 days later
-        rooms: [
-          {
-            id: '7709411_91461863_1_1_0',
-            count: 1,
-          },
-          {
-            id: '7709404_91461863_0_1_0',
-            count: 2,
-          },
-        ],
-        language: 'en',
-        currency: 'EUR',
-        version: 'rn-test',
-      },
-      'http://test.url.lol',
-    ),
-  ).toMatchSnapshot();
-
-  expect(
-    createURI(
-      {
-        hotelId: '2',
-        checkin: new Date(2 * ONE_DAY),
-        checkout: new Date(16 * ONE_DAY), // 14 days later
-        rooms: [
-          {
-            id: '7709411_91461863_1_1_0',
-            count: 5,
-          },
-        ],
-        language: 'cs',
-        currency: 'JPY',
-        version: 'rn-test',
-      },
-      'http://test.url.lol',
-    ),
-  ).toMatchSnapshot();
-});
-
-it('logs ancillaryPurchased event', () => {
-  jest.resetAllMocks();
-  // $FlowExpectedError: We don't need props for this test
-  const Component = new PaymentScreen({});
-  const loadingEvent = {
-    loading: true,
-    url: 'https://secure.booking.com/confirmation-en-gb.com',
-  };
-
-  const loadingDoneEvent = {
-    loading: false,
-    url: 'https://secure.booking.com/confirmation-en-gb.com',
-  };
-
-  const badUrlEvent = {
-    loading: false,
-    url: 'https://secure.booking.com/book.html',
-  };
-
-  Component.onNavigationStateChange(loadingDoneEvent);
-  Component.onNavigationStateChange(badUrlEvent);
-  Component.onNavigationStateChange(loadingEvent);
-
-  expect(
-    NativeModules.RNLoggingModule.ancillaryPurchased,
-  ).toHaveBeenCalledTimes(1);
-});
diff --git a/app/hotels/src/singleHotel/paymentScreen/__tests__/BookingPaymentScreen.test.js b/app/hotels/src/singleHotel/paymentScreen/__tests__/BookingPaymentScreen.test.js
new file mode 100644
index 00000000..8bfff8d9
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/__tests__/BookingPaymentScreen.test.js
@@ -0,0 +1,43 @@
+// @flow
+
+import { BookingPaymentScreen } from '../BookingPaymentScreen';
+
+const ONE_DAY = 8.64e7; // ms
+
+it('creates correct URL', () => {
+  expect(
+    new BookingPaymentScreen({
+      hotelId: '1',
+      checkin: new Date(ONE_DAY),
+      checkout: new Date(3 * ONE_DAY), // 2 days later
+      roomConfig: [
+        {
+          roomId: '7709411_91461863_1_1_0',
+          count: 1,
+        },
+        {
+          roomId: '7709404_91461863_0_1_0',
+          count: 2,
+        },
+      ],
+      currency: 'EUR',
+      version: 'rn-test',
+    }).createURI('http://test.url.lol'),
+  ).toMatchSnapshot();
+
+  expect(
+    new BookingPaymentScreen({
+      hotelId: '2',
+      checkin: new Date(2 * ONE_DAY),
+      checkout: new Date(16 * ONE_DAY), // 14 days later
+      roomConfig: [
+        {
+          roomId: '7709411_91461863_1_1_0',
+          count: 5,
+        },
+      ],
+      currency: 'JPY',
+      version: 'rn-test',
+    }).createURI('http://test.url.lol'),
+  ).toMatchSnapshot();
+});
diff --git a/app/hotels/src/singleHotel/paymentScreen/__tests__/PaymentWebView.test.js b/app/hotels/src/singleHotel/paymentScreen/__tests__/PaymentWebView.test.js
new file mode 100644
index 00000000..9b14b4ef
--- /dev/null
+++ b/app/hotels/src/singleHotel/paymentScreen/__tests__/PaymentWebView.test.js
@@ -0,0 +1,33 @@
+// @flow
+
+import { NativeModules } from 'react-native';
+
+import { PaymentWebView } from '../PaymentWebView';
+
+it('logs ancillaryPurchased event', () => {
+  jest.resetAllMocks();
+  // $FlowExpectedError: We don't need props for this test
+  const Component = new PaymentWebView({});
+  const loadingEvent = {
+    loading: true,
+    url: 'https://secure.booking.com/confirmation-en-gb.com',
+  };
+
+  const loadingDoneEvent = {
+    loading: false,
+    url: 'https://secure.booking.com/confirmation-en-gb.com',
+  };
+
+  const badUrlEvent = {
+    loading: false,
+    url: 'https://secure.booking.com/book.html',
+  };
+
+  Component.onNavigationStateChange(loadingDoneEvent);
+  Component.onNavigationStateChange(badUrlEvent);
+  Component.onNavigationStateChange(loadingEvent);
+
+  expect(
+    NativeModules.RNLoggingModule.ancillaryPurchased,
+  ).toHaveBeenCalledTimes(1);
+});
diff --git a/app/hotels/src/singleHotel/__tests__/__snapshots__/PaymentScreen.test.js.snap b/app/hotels/src/singleHotel/paymentScreen/__tests__/__snapshots__/BookingPaymentScreen.test.js.snap
similarity index 81%
rename from app/hotels/src/singleHotel/__tests__/__snapshots__/PaymentScreen.test.js.snap
rename to app/hotels/src/singleHotel/paymentScreen/__tests__/__snapshots__/BookingPaymentScreen.test.js.snap
index 5663c594..9cb9a170 100644
--- a/app/hotels/src/singleHotel/__tests__/__snapshots__/PaymentScreen.test.js.snap
+++ b/app/hotels/src/singleHotel/paymentScreen/__tests__/__snapshots__/BookingPaymentScreen.test.js.snap
@@ -2,4 +2,4 @@
 
 exports[`creates correct URL 1`] = `"http://test.url.lol&checkin=1970-01-02&interval=2&label=kiwi-ios-react-rn-test&lang=en&selected_currency=EUR"`;
 
-exports[`creates correct URL 2`] = `"http://test.url.lol&checkin=1970-01-03&interval=14&label=kiwi-ios-react-rn-test&lang=cs&selected_currency=JPY"`;
+exports[`creates correct URL 2`] = `"http://test.url.lol&checkin=1970-01-03&interval=14&label=kiwi-ios-react-rn-test&lang=en&selected_currency=JPY"`;