2023-01-04 15:24:58 +01:00
/ *
This file is part of GNU Taler
( C ) 2022 Taler Systems S . A .
GNU Taler is free software ; you can redistribute it and / or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation ; either version 3 , or ( at your option ) any later version .
GNU Taler is distributed in the hope that it will be useful , but WITHOUT ANY
WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE . See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License along with
GNU Taler ; see the file COPYING . If not , see < http : / / www.gnu.org / licenses / >
* /
import {
AmountJson ,
Amounts ,
2023-01-27 21:34:18 +01:00
PayMerchantInsufficientBalanceDetails ,
2023-01-04 15:24:58 +01:00
PreparePayResult ,
PreparePayResultType ,
2023-01-09 12:38:48 +01:00
TranslatedString ,
2023-01-04 15:24:58 +01:00
} from "@gnu-taler/taler-util" ;
import { Fragment , h , VNode } from "preact" ;
import { useState } from "preact/hooks" ;
import { Amount } from "./Amount.js" ;
import { Part } from "./Part.js" ;
import { QR } from "./QR.js" ;
import { LinkSuccess , WarningBox } from "./styled/index.js" ;
import { useTranslationContext } from "../context/translation.js" ;
import { Button } from "../mui/Button.js" ;
import { ButtonHandler } from "../mui/handlers.js" ;
import { assertUnreachable } from "../utils/index.js" ;
interface Props {
payStatus : PreparePayResult ;
payHandler : ButtonHandler | undefined ;
uri : string ;
amount : AmountJson ;
goToWalletManualWithdraw : ( currency : string ) = > Promise < void > ;
}
export function PaymentButtons ( {
payStatus ,
uri ,
payHandler ,
amount ,
goToWalletManualWithdraw ,
} : Props ) : VNode {
const { i18n } = useTranslationContext ( ) ;
if ( payStatus . status === PreparePayResultType . PaymentPossible ) {
const privateUri = ` ${ uri } &n= ${ payStatus . noncePriv } ` ;
return (
< Fragment >
< section >
< Button
variant = "contained"
color = "success"
onClick = { payHandler ? . onClick }
>
< i18n.Translate >
Pay & nbsp ;
{ < Amount value = { amount } / > }
< / i18n.Translate >
< / Button >
< / section >
< PayWithMobile uri = { privateUri } / >
< / Fragment >
) ;
}
if ( payStatus . status === PreparePayResultType . InsufficientBalance ) {
2023-01-27 21:34:18 +01:00
const reason = getReason ( payStatus . balanceDetails ) ;
2023-01-04 15:24:58 +01:00
let BalanceMessage = "" ;
2023-01-27 21:34:18 +01:00
switch ( reason ) {
case "age-acceptable" : {
BalanceMessage = i18n . str ` Balance is not enough because you have ${ Amounts . stringifyValue (
payStatus . balanceDetails . balanceAgeAcceptable ,
) } $ { amount . currency } to pay for contracts restricted for age above $ {
payStatus . contractTerms . minimum_age
} years old ` ;
break ;
}
case "available" : {
BalanceMessage = i18n . str ` Balance is not enough because you have ${ Amounts . stringifyValue (
payStatus . balanceDetails . balanceAvailable ,
) } $ { amount . currency } available . ` ;
break ;
}
case "merchant-acceptable" : {
BalanceMessage = i18n . str ` Balance is not enough because merchant will just accept ${ Amounts . stringifyValue (
payStatus . balanceDetails . balanceMerchantAcceptable ,
) } $ {
amount . currency
} . To know more you can check which exchange and auditors the merchant trust . ` ;
break ;
}
case "merchant-depositable" : {
BalanceMessage = i18n . str ` Balance is not enough because merchant will just accept ${ Amounts . stringifyValue (
payStatus . balanceDetails . balanceMerchantDepositable ,
) } $ {
amount . currency
} . To know more you can check which wire methods the merchant accepts . ` ;
break ;
2023-01-04 15:24:58 +01:00
}
2023-01-27 21:34:18 +01:00
case "material" : {
BalanceMessage = i18n . str ` Balance is not enough because you have ${ Amounts . stringifyValue (
payStatus . balanceDetails . balanceMaterial ,
) } $ {
amount . currency
} to spend right know . There are some coins that need to be refreshed . ` ;
break ;
}
case "fee-gap" : {
BalanceMessage = i18n . str ` Balance looks like it should be enough, but doesn't cover all fees requested by the merchant and payment processor. Please ensure there is at least ${ Amounts . stringifyValue (
payStatus . balanceDetails . feeGapEstimate ,
) } $ {
amount . currency
} more balance in your wallet or ask your merchant to cover more of the fees . ` ;
break ;
}
default :
assertUnreachable ( reason ) ;
2023-01-04 15:24:58 +01:00
}
const uriPrivate = ` ${ uri } &n= ${ payStatus . noncePriv } ` ;
return (
< Fragment >
< section >
< WarningBox > { BalanceMessage } < / WarningBox >
< / section >
< section >
< Button
variant = "contained"
color = "success"
onClick = { ( ) = > goToWalletManualWithdraw ( Amounts . stringify ( amount ) ) }
>
< i18n.Translate > Get digital cash < / i18n.Translate >
< / Button >
< / section >
< PayWithMobile uri = { uriPrivate } / >
< / Fragment >
) ;
}
if ( payStatus . status === PreparePayResultType . AlreadyConfirmed ) {
return (
< Fragment >
< section >
{ payStatus . paid && payStatus . contractTerms . fulfillment_message && (
< Part
2023-01-09 12:38:48 +01:00
title = { i18n . str ` Merchant message ` }
text = {
payStatus . contractTerms . fulfillment_message as TranslatedString
}
2023-01-04 15:24:58 +01:00
kind = "neutral"
/ >
) }
< / section >
{ ! payStatus . paid && < PayWithMobile uri = { uri } / > }
< / Fragment >
) ;
}
assertUnreachable ( payStatus ) ;
}
function PayWithMobile ( { uri } : { uri : string } ) : VNode {
const { i18n } = useTranslationContext ( ) ;
const [ showQR , setShowQR ] = useState < boolean > ( false ) ;
return (
< section >
< LinkSuccess upperCased onClick = { ( ) = > setShowQR ( ( qr ) = > ! qr ) } >
2023-01-09 12:38:48 +01:00
{ ! showQR ? i18n . str ` Pay with a mobile phone ` : i18n . str ` Hide QR ` }
2023-01-04 15:24:58 +01:00
< / LinkSuccess >
{ showQR && (
< div >
< QR text = { uri } / >
< i18n.Translate >
Scan the QR code or & nbsp ;
< a href = { uri } >
< i18n.Translate > click here < / i18n.Translate >
< / a >
< / i18n.Translate >
< / div >
) }
< / section >
) ;
}
2023-01-27 21:34:18 +01:00
type NoEnoughBalanceReason =
| "available"
| "material"
| "age-acceptable"
| "merchant-acceptable"
| "merchant-depositable"
| "fee-gap" ;
function getReason (
info : PayMerchantInsufficientBalanceDetails ,
) : NoEnoughBalanceReason {
if ( Amounts . cmp ( info . amountRequested , info . balanceAvailable ) ) {
return "available" ;
}
if ( Amounts . cmp ( info . amountRequested , info . balanceMaterial ) ) {
return "material" ;
}
if ( Amounts . cmp ( info . amountRequested , info . balanceAgeAcceptable ) ) {
return "age-acceptable" ;
}
if ( Amounts . cmp ( info . amountRequested , info . balanceMerchantAcceptable ) ) {
return "merchant-acceptable" ;
}
if ( Amounts . cmp ( info . amountRequested , info . balanceMerchantDepositable ) ) {
return "merchant-depositable" ;
}
return "fee-gap" ;
}