From 3b61edcc5a6dca2893ed11d36978f63931657c7f Mon Sep 17 00:00:00 2001 From: M Date: Mon, 18 Jul 2022 16:23:53 +0100 Subject: [PATCH] Add payment status update for ionia. --- lib/di.dart | 15 ++ lib/ionia/ionia_any_pay_payment_info.dart | 9 + lib/ionia/ionia_anypay.dart | 7 +- lib/ionia/ionia_api.dart | 43 +++ lib/ionia/ionia_order.dart | 2 +- lib/ionia/ionia_service.dart | 10 + lib/router.dart | 11 + lib/routes.dart | 1 + .../cards/ionia_buy_card_detail_page.dart | 18 +- .../cards/ionia_payment_status_page.dart | 249 ++++++++++++++++++ .../ionia_payment_status_view_model.dart | 58 ++++ .../ionia_purchase_merch_view_model.dart | 11 +- res/values/strings_en.arb | 9 +- 13 files changed, 423 insertions(+), 20 deletions(-) create mode 100644 lib/ionia/ionia_any_pay_payment_info.dart create mode 100644 lib/src/screens/ionia/cards/ionia_payment_status_page.dart create mode 100644 lib/view_model/ionia/ionia_payment_status_view_model.dart diff --git a/lib/di.dart b/lib/di.dart index 4451649b1..76d21e145 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -146,6 +146,10 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; import 'package:cake_wallet/ionia/ionia_token_service.dart'; import 'package:cake_wallet/anypay/anypay_api.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart'; +import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart'; +import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; +import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; final getIt = GetIt.instance; @@ -741,5 +745,16 @@ Future setup( getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get())); + getIt.registerFactoryParam( + (IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo) + => IoniaPaymentStatusViewModel( + getIt.get(), + paymentInfo: paymentInfo, + committedInfo: committedInfo)); + + getIt.registerFactoryParam( + (IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo) + => IoniaPaymentStatusPage(getIt.get(param1: paymentInfo, param2: committedInfo))); + _isSetupFinished = true; } diff --git a/lib/ionia/ionia_any_pay_payment_info.dart b/lib/ionia/ionia_any_pay_payment_info.dart new file mode 100644 index 000000000..6146a46fe --- /dev/null +++ b/lib/ionia/ionia_any_pay_payment_info.dart @@ -0,0 +1,9 @@ +import 'package:cake_wallet/anypay/any_pay_payment.dart'; +import 'package:cake_wallet/ionia/ionia_order.dart'; + +class IoniaAnyPayPaymentInfo { + const IoniaAnyPayPaymentInfo(this.ioniaOrder, this.anyPayPayment); + + final IoniaOrder ioniaOrder; + final AnyPayPayment anyPayPayment; +} diff --git a/lib/ionia/ionia_anypay.dart b/lib/ionia/ionia_anypay.dart index d4b6d4d59..b9b53498a 100644 --- a/lib/ionia/ionia_anypay.dart +++ b/lib/ionia/ionia_anypay.dart @@ -13,6 +13,8 @@ import 'package:cake_wallet/anypay/any_pay_trasnaction.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; +import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; +import 'package:cake_wallet/ionia/ionia_order.dart'; class IoniaAnyPay { IoniaAnyPay(this.ioniaService, this.anyPayApi, this.wallet); @@ -21,14 +23,15 @@ class IoniaAnyPay { final AnyPayApi anyPayApi; final WalletBase wallet; - Future purchase({ + Future purchase({ @required String merchId, @required double amount}) async { final invoice = await ioniaService.purchaseGiftCard( merchId: merchId, amount: amount, currency: wallet.currency.title.toUpperCase()); - return anyPayApi.paymentRequest(invoice.uri); + final anypayPayment = await anyPayApi.paymentRequest(invoice.uri); + return IoniaAnyPayPaymentInfo(invoice, anypayPayment); } Future commitInvoice(AnyPayPayment payment) async { diff --git a/lib/ionia/ionia_api.dart b/lib/ionia/ionia_api.dart index da5911c84..72312f085 100644 --- a/lib/ionia/ionia_api.dart +++ b/lib/ionia/ionia_api.dart @@ -21,6 +21,7 @@ class IoniaApi { static final getCurrentUserGiftCardSummariesUrl = Uri.https(baseUri, '/$pathPrefix/GetCurrentUserGiftCardSummaries'); static final changeGiftCardUrl = Uri.https(baseUri, '/$pathPrefix/ChargeGiftCard'); static final getGiftCardUrl = Uri.https(baseUri, '/$pathPrefix/GetGiftCard'); + static final getPaymentStatusUrl = Uri.https(baseUri, '/$pathPrefix/PaymentStatus'); // Create user @@ -355,4 +356,46 @@ class IoniaApi { final data = decodedBody['Data'] as Map; return IoniaGiftCard.fromJsonMap(data); } + + // Payment Status + + Future getPaymentStatus({ + @required String username, + @required String password, + @required String clientId, + @required String orderId, + @required String paymentId}) async { + final headers = { + 'clientId': clientId, + 'username': username, + 'password': password, + 'Content-Type': 'application/json'}; + final body = { + 'order_id': orderId, + 'paymentId': paymentId}; + final response = await post( + getPaymentStatusUrl, + headers: headers, + body: json.encode(body)); + + if (response.statusCode != 200) { + throw Exception('Failed to get Payment Status for order_id ${orderId} paymentId ${paymentId};Incorrect response status: ${response.statusCode};'); + } + + final decodedBody = json.decode(response.body) as Map; + final isSuccessful = decodedBody['Successful'] as bool ?? false; + + if (!isSuccessful) { + final msg = decodedBody['ErrorMessage'] as String ?? ''; + + if (msg.isNotEmpty) { + throw Exception(msg); + } + + throw Exception('Failed to get Payment Status for order_id ${orderId} paymentId ${paymentId}'); + } + + final data = decodedBody['Data'] as Map; + return data['gift_card_id'] as int; + } } \ No newline at end of file diff --git a/lib/ionia/ionia_order.dart b/lib/ionia/ionia_order.dart index 691327e43..f9c35ea70 100644 --- a/lib/ionia/ionia_order.dart +++ b/lib/ionia/ionia_order.dart @@ -12,7 +12,7 @@ class IoniaOrder { uri: obj['uri'] as String, currency: obj['currency'] as String, amount: obj['amount'] as double, - paymentId: obj['payment_id'] as String); + paymentId: obj['paymentId'] as String); } final String id; diff --git a/lib/ionia/ionia_service.dart b/lib/ionia/ionia_service.dart index c00b0ad18..b5ed5a495 100644 --- a/lib/ionia/ionia_service.dart +++ b/lib/ionia/ionia_service.dart @@ -149,4 +149,14 @@ class IoniaService { final password = await secureStorage.read(key: ioniaPasswordStorageKey); return ioniaApi.getGiftCard(username: username, password: password, clientId: clientId,id: id); } + + // Payment Status + + Future getPaymentStatus({ + @required String orderId, + @required String paymentId}) async { + final username = await secureStorage.read(key: ioniaUsernameStorageKey); + final password = await secureStorage.read(key: ioniaPasswordStorageKey); + return ioniaApi.getPaymentStatus(username: username, password: password, clientId: clientId, orderId: orderId, paymentId: paymentId); + } } \ No newline at end of file diff --git a/lib/router.dart b/lib/router.dart index 9764ec17a..aa2b53cd9 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -75,6 +75,9 @@ import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; import 'package:cake_wallet/src/screens/ionia/ionia.dart'; +import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; +import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; +import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; RouteSettings currentRouteSettings; @@ -451,6 +454,14 @@ Route createRoute(RouteSettings settings) { final args = settings.arguments as List; return CupertinoPageRoute(builder: (_) => getIt.get(param1: args.first)); + case Routes.ioniaPaymentStatusPage: + final args = settings.arguments as List; + final paymentInfo = args.first as IoniaAnyPayPaymentInfo; + final commitedInfo = args[1] as AnyPayPaymentCommittedInfo; + return CupertinoPageRoute(builder: (_) => getIt.get( + param1: paymentInfo, + param2: commitedInfo)); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 1b04db1eb..bd070cc2b 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -73,4 +73,5 @@ class Routes { static const ioniaAccountCardsPage = 'ionia_account_cards_page'; static const ioniaCustomTipPage = 'ionia_custom_tip_page'; static const ioniaGiftCardDetailPage = '/ionia_gift_card_detail_page'; + static const ioniaPaymentStatusPage = '/ionia_payment_status_page'; } diff --git a/lib/src/screens/ionia/cards/ionia_buy_card_detail_page.dart b/lib/src/screens/ionia/cards/ionia_buy_card_detail_page.dart index b27dc9a5a..abf9b3032 100644 --- a/lib/src/screens/ionia/cards/ionia_buy_card_detail_page.dart +++ b/lib/src/screens/ionia/cards/ionia_buy_card_detail_page.dart @@ -1,11 +1,11 @@ import 'dart:ui'; - import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/ionia/ionia_merchant.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/ionia/widgets/confirm_modal.dart'; import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; @@ -109,18 +109,12 @@ class IoniaBuyGiftCardDetailPage extends StatelessWidget { } if (state is ExecutedSuccessfullyState) { - final transactionInfo = state.payload as AnyPayPaymentCommittedInfo; WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( - context: context, - barrierDismissible: true, - barrierColor: PaletteDark.darkNightBlue.withOpacity(0.75), - builder: (BuildContext context) { - return Center( - child: _IoniaTransactionCommitedAlert(transactionInfo: transactionInfo), - ); - }, - ); + Navigator.of(context).pushReplacementNamed( + Routes.ioniaPaymentStatusPage, + arguments: [ + ioniaPurchaseViewModel.paymentInfo, + ioniaPurchaseViewModel.committedInfo]); }); } }); diff --git a/lib/src/screens/ionia/cards/ionia_payment_status_page.dart b/lib/src/screens/ionia/cards/ionia_payment_status_page.dart new file mode 100644 index 000000000..92bc62b91 --- /dev/null +++ b/lib/src/screens/ionia/cards/ionia_payment_status_page.dart @@ -0,0 +1,249 @@ +import 'package:cake_wallet/ionia/ionia_gift_card.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:cake_wallet/typography.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; + +class IoniaPaymentStatusPage extends BasePage { + IoniaPaymentStatusPage(this.viewModel); + + final IoniaPaymentStatusViewModel viewModel; + + @override + Widget middle(BuildContext context) { + return Text( + S.of(context).generating_gift_card, + textAlign: TextAlign.center, + style: textMediumSemiBold( + color: Theme.of(context).accentTextTheme.display4.backgroundColor)); + } + + @override + Widget body(BuildContext context) { + return _IoniaPaymentStatusPageBody(viewModel); + } +} + +class _IoniaPaymentStatusPageBody extends StatefulWidget { + _IoniaPaymentStatusPageBody(this.viewModel); + + final IoniaPaymentStatusViewModel viewModel; + + @override + _IoniaPaymentStatusPageBodyBodyState createState() => _IoniaPaymentStatusPageBodyBodyState(); +} + +class _IoniaPaymentStatusPageBodyBodyState extends State<_IoniaPaymentStatusPageBody> { + ReactionDisposer _onErrorReaction; + ReactionDisposer _onGiftCardReaction; + + @override + void initState() { + if (widget.viewModel.giftCard != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context) + .pushReplacementNamed(Routes.ioniaGiftCardDetailPage, arguments: [widget.viewModel.giftCard]); + }); + } + + if (widget.viewModel.error != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: widget.viewModel.error, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + }); + } + + _onErrorReaction = reaction((_) => widget.viewModel.error, (String error) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: error, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + }); + }); + + _onGiftCardReaction = reaction((_) => widget.viewModel.giftCard, (IoniaGiftCard giftCard) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context) + .pushReplacementNamed(Routes.ioniaGiftCardDetailPage, arguments: [giftCard]); + }); + }); + + super.initState(); + } + + @override + void dispose() { + _onErrorReaction?.reaction?.dispose(); + _onGiftCardReaction?.reaction?.dispose(); + widget.viewModel.timer.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ScrollableWithBottomSection( + contentPadding: EdgeInsets.all(24), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row(children: [ + Padding( + padding: EdgeInsets.only(right: 10), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.green), + height: 10, + width: 10)), + Text( + S.of(context).awaiting_payment_confirmation, + style: textLargeSemiBold( + color: Theme.of(context).primaryTextTheme.title.color)) + ]), + SizedBox(height: 40), + Row(children: [ + SizedBox(width: 20), + Expanded(child: + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ...widget.viewModel + .committedInfo + .transactions + .map((transaction) => buildDescriptionTileWithCopy(context, S.of(context).transaction_details_transaction_id, transaction.id)), + Divider(height: 30), + buildDescriptionTileWithCopy(context, S.of(context).order_id, widget.viewModel.paymentInfo.ioniaOrder.id), + Divider(height: 30), + buildDescriptionTileWithCopy(context, S.of(context).payment_id, widget.viewModel.paymentInfo.ioniaOrder.paymentId), + ])) + ]), + SizedBox(height: 40), + Observer(builder: (_) { + if (widget.viewModel.giftCard != null) { + return Container( + padding: EdgeInsets.only(top: 40), + child: Row(children: [ + Padding( + padding: EdgeInsets.only(right: 10,), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.green), + height: 10, + width: 10)), + Text( + S.of(context).gift_card_is_generated, + style: textLargeSemiBold( + color: Theme.of(context).primaryTextTheme.title.color)) + ])); + } + + return Row(children: [ + Padding( + padding: EdgeInsets.only(right: 10), + child: Observer(builder: (_) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: widget.viewModel.giftCard == null ? Colors.grey : Colors.green), + height: 10, + width: 10); + })), + Text( + S.of(context).generating_gift_card, + style: textLargeSemiBold( + color: Theme.of(context).primaryTextTheme.title.color))]); + }), + ], + ), + bottomSection: Padding( + padding: EdgeInsets.only(bottom: 12), + child: Column(children: [ + Container( + padding: EdgeInsets.only(left: 40, right: 40, bottom: 20), + child: Text( + S.of(context).proceed_after_one_minute, + style: textMedium( + color: Theme.of(context).primaryTextTheme.title.color, + ).copyWith(fontWeight: FontWeight.w500), + textAlign: TextAlign.center, + )), + Observer(builder: (_) { + if (widget.viewModel.giftCard != null) { + return PrimaryButton( + onPressed: () => Navigator.of(context) + .pushReplacementNamed( + Routes.ioniaGiftCardDetailPage, + arguments: [widget.viewModel.giftCard]), + text: S.of(context).open_gift_card, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white); + } + + return PrimaryButton( + onPressed: () => Navigator.of(context).pushNamed(Routes.support), + text: S.of(context).contact_support, + color: Theme.of(context).accentTextTheme.caption.color, + textColor: Theme.of(context).primaryTextTheme.title.color); + }) + ]) + ), + ); + } + + Widget buildDescriptionTile(BuildContext context, String title, String subtitle, VoidCallback onTap) { + return GestureDetector( + onTap: () => onTap(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: textXSmall( + color: Theme.of(context).primaryTextTheme.overline.color, + ), + ), + SizedBox(height: 8), + Text( + subtitle, + style: textMedium( + color: Theme.of(context).primaryTextTheme.title.color, + ), + ), + ], + )); + } + + Widget buildDescriptionTileWithCopy(BuildContext context, String title, String subtitle) { + return buildDescriptionTile(context, title, subtitle, () { + Clipboard.setData(ClipboardData(text: subtitle)); + showBar(context, + S.of(context).transaction_details_copied(title)); + }); + } +} \ No newline at end of file diff --git a/lib/view_model/ionia/ionia_payment_status_view_model.dart b/lib/view_model/ionia/ionia_payment_status_view_model.dart new file mode 100644 index 000000000..187e24856 --- /dev/null +++ b/lib/view_model/ionia/ionia_payment_status_view_model.dart @@ -0,0 +1,58 @@ +import 'dart:async'; +import 'package:mobx/mobx.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cake_wallet/ionia/ionia_service.dart'; +import 'package:cake_wallet/ionia/ionia_gift_card.dart'; +import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; +import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; + +part 'ionia_payment_status_view_model.g.dart'; + +class IoniaPaymentStatusViewModel = IoniaPaymentStatusViewModelBase with _$IoniaPaymentStatusViewModel; + +abstract class IoniaPaymentStatusViewModelBase with Store { + IoniaPaymentStatusViewModelBase( + this.ioniaService,{ + @required this.paymentInfo, + @required this.committedInfo}) { + _timer = Timer.periodic(updateTime, (timer) async { + await updatePaymentStatus(); + + if (giftCard != null) { + timer?.cancel(); + } + }); + } + + static const updateTime = Duration(seconds: 3); + + final IoniaService ioniaService; + final IoniaAnyPayPaymentInfo paymentInfo; + final AnyPayPaymentCommittedInfo committedInfo; + + @observable + IoniaGiftCard giftCard; + + @observable + String error; + + Timer get timer => _timer; + + Timer _timer; + + @action + Future updatePaymentStatus() async { + try { + final giftCardId = await ioniaService.getPaymentStatus( + orderId: paymentInfo.ioniaOrder.id, + paymentId: paymentInfo.ioniaOrder.paymentId); + + if (giftCardId != null) { + giftCard = await ioniaService.getGiftCard(id: giftCardId); + } + + } catch (e) { + error = e.toString(); + } + } +} diff --git a/lib/view_model/ionia/ionia_purchase_merch_view_model.dart b/lib/view_model/ionia/ionia_purchase_merch_view_model.dart index 6a65a49e9..1e537ac37 100644 --- a/lib/view_model/ionia/ionia_purchase_merch_view_model.dart +++ b/lib/view_model/ionia/ionia_purchase_merch_view_model.dart @@ -1,11 +1,12 @@ +import 'package:flutter/foundation.dart'; +import 'package:mobx/mobx.dart'; import 'package:cake_wallet/anypay/any_pay_payment.dart'; import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_merchant.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; -import 'package:flutter/foundation.dart'; -import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; part 'ionia_purchase_merch_view_model.g.dart'; @@ -38,7 +39,9 @@ abstract class IoniaMerchPurchaseViewModelBase with Store { final IoniaAnyPay ioniaAnyPayService; - AnyPayPayment invoice; + IoniaAnyPayPaymentInfo paymentInfo; + + AnyPayPayment get invoice => paymentInfo?.anyPayPayment; AnyPayPaymentCommittedInfo committedInfo; @@ -67,7 +70,7 @@ abstract class IoniaMerchPurchaseViewModelBase with Store { Future createInvoice() async { try { invoiceCreationState = IsExecutingState(); - invoice = await ioniaAnyPayService.purchase(merchId: ioniaMerchant.id.toString(), amount: giftCardAmount); + paymentInfo = await ioniaAnyPayService.purchase(merchId: ioniaMerchant.id.toString(), amount: giftCardAmount); invoiceCreationState = ExecutedSuccessfullyState(); } catch (e) { invoiceCreationState = FailureState(e.toString()); diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index b3e683946..80421655c 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -620,5 +620,12 @@ "awaiting_payment_confirmation": "Awaiting payment confirmation", "transaction_sent_notice": "If the screen doesn’t proceed after 1 minute, check a block explorer and your email.", "agree": "Agree", - "in_store": "In Store" + "in_store": "In Store", + "generating_gift_card": "Generating Gift Card", + "payment_was_received": "Your payment was received.", + "proceed_after_one_minute": "If the screen doesn’t proceed after 1 minute, check your email.", + "order_id": "Order ID", + "gift_card_is_generated": "Gift Card is generated", + "open_gift_card": "Open Gift Card", + "contact_support": "Contact Support" }