Payment updates

This commit is contained in:
Ali 2022-05-31 15:11:42 +04:00
parent 1b0b7660db
commit 6da7ed5bfb
7 changed files with 333 additions and 71 deletions

View File

@ -7665,3 +7665,5 @@ Sorry for the inconvenience.";
"Premium.Limits.FoldersInfo" = "Organize your chats into 20 folders";
"Premium.Limits.ChatsPerFolderInfo" = "Add up to 200 chats into each of your folders";
"Premium.Limits.AccountsInfo" = "Connect 4 accounts with different mobile numbers";
"Bot.AccepRecurrentInfo" = "I accept [Terms of Service]() of **%1$@**";

View File

@ -25,6 +25,9 @@ swift_library(
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/OverlayStatusController:OverlayStatusController",
"//submodules/ShimmerEffect:ShimmerEffect",
"//submodules/CheckNode:CheckNode",
"//submodules/TextFormat:TextFormat",
"//submodules/Markdown:Markdown",
],
visibility = [
"//visibility:public",

View File

@ -6,8 +6,8 @@ import PassKit
import ShimmerEffect
enum BotCheckoutActionButtonState: Equatable {
case active(String)
case applePay
case active(text: String, isEnabled: Bool)
case applePay(isEnabled: Bool)
case placeholder
}
@ -17,6 +17,7 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
static var height: CGFloat = 52.0
private var activeFillColor: UIColor
private var inactiveFillColor: UIColor
private var foregroundColor: UIColor
private let activeBackgroundNode: ASImageNode
@ -28,17 +29,23 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
private var placeholderNode: ShimmerEffectNode?
init(activeFillColor: UIColor, foregroundColor: UIColor) {
private var activeImage: UIImage?
private var inactiveImage: UIImage?
init(activeFillColor: UIColor, inactiveFillColor: UIColor, foregroundColor: UIColor) {
self.activeFillColor = activeFillColor
self.inactiveFillColor = inactiveFillColor
self.foregroundColor = foregroundColor
let diameter: CGFloat = 20.0
self.activeImage = generateStretchableFilledCircleImage(diameter: diameter, color: activeFillColor)
self.inactiveImage = generateStretchableFilledCircleImage(diameter: diameter, color: inactiveFillColor)
self.activeBackgroundNode = ASImageNode()
self.activeBackgroundNode.displaysAsynchronously = false
self.activeBackgroundNode.displayWithoutProcessing = true
self.activeBackgroundNode.isLayerBacked = true
self.activeBackgroundNode.image = generateStretchableFilledCircleImage(diameter: diameter, color: activeFillColor)
self.activeBackgroundNode.image = self.activeImage
self.labelNode = TextNode()
self.labelNode.displaysAsynchronously = false
@ -75,7 +82,7 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
var labelSize = self.labelNode.bounds.size
if let state = self.state {
switch state {
case let .active(title):
case let .active(title, isEnabled):
if let applePayButton = self.applePayButton {
self.applePayButton = nil
applePayButton.removeFromSuperview()
@ -85,12 +92,20 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
self.placeholderNode = nil
placeholderNode.removeFromSupernode()
}
let image = isEnabled ? self.activeImage : self.inactiveImage
if let image = image, let currentImage = self.activeBackgroundNode.image, currentImage !== image {
self.activeBackgroundNode.image = image
self.activeBackgroundNode.layer.animate(from: currentImage.cgImage! as AnyObject, to: image.cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2)
} else {
self.activeBackgroundNode.image = image
}
let makeLayout = TextNode.asyncLayout(self.labelNode)
let (labelLayout, labelApply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: self.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: size, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let _ = labelApply()
labelSize = labelLayout.size
case .applePay:
case let .applePay(isEnabled):
if self.applePayButton == nil {
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
let applePayButton: PKPaymentButton
@ -102,6 +117,7 @@ final class BotCheckoutActionButton: HighlightableButtonNode {
applePayButton.addTarget(self, action: #selector(self.applePayButtonPressed), for: .touchUpInside)
self.view.addSubview(applePayButton)
self.applePayButton = applePayButton
applePayButton.isEnabled = isEnabled
}
}

View File

@ -15,13 +15,16 @@ public final class BotCheckoutController: ViewController {
let form: BotPaymentForm
let validatedFormInfo: BotPaymentValidatedFormInfo?
let botPeer: EnginePeer?
private init(
form: BotPaymentForm,
validatedFormInfo: BotPaymentValidatedFormInfo?
validatedFormInfo: BotPaymentValidatedFormInfo?,
botPeer: EnginePeer?
) {
self.form = form
self.validatedFormInfo = validatedFormInfo
self.botPeer = botPeer
}
public static func fetch(context: AccountContext, source: BotPaymentInvoiceSource) -> Signal<InputData, FetchError> {
@ -39,28 +42,35 @@ public final class BotCheckoutController: ViewController {
return .generic
}
|> mapToSignal { paymentForm -> Signal<InputData, FetchError> in
if let current = paymentForm.savedInfo {
return context.engine.payments.validateBotPaymentForm(saveInfo: true, source: source, formInfo: current)
|> mapError { _ -> FetchError in
return .generic
}
|> map { result -> InputData in
return InputData(
form: paymentForm,
validatedFormInfo: result
)
}
|> `catch` { _ -> Signal<InputData, FetchError> in
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: paymentForm.paymentBotId))
|> castError(FetchError.self)
|> mapToSignal { botPeer -> Signal<InputData, FetchError> in
if let current = paymentForm.savedInfo {
return context.engine.payments.validateBotPaymentForm(saveInfo: true, source: source, formInfo: current)
|> mapError { _ -> FetchError in
return .generic
}
|> map { result -> InputData in
return InputData(
form: paymentForm,
validatedFormInfo: result,
botPeer: botPeer
)
}
|> `catch` { _ -> Signal<InputData, FetchError> in
return .single(InputData(
form: paymentForm,
validatedFormInfo: nil,
botPeer: botPeer
))
}
} else {
return .single(InputData(
form: paymentForm,
validatedFormInfo: nil
validatedFormInfo: nil,
botPeer: botPeer
))
}
} else {
return .single(InputData(
form: paymentForm,
validatedFormInfo: nil
))
}
}
}

View File

@ -17,6 +17,9 @@ import PasswordSetupUI
import Stripe
import LocalAuth
import OverlayStatusController
import CheckNode
import TextFormat
import Markdown
final class BotCheckoutControllerArguments {
fileprivate let account: Account
@ -489,6 +492,160 @@ private func availablePaymentMethods(form: BotPaymentForm, current: BotCheckoutP
return methods
}
private final class RecurrentConfirmationNode: ASDisplayNode {
private let isAcceptedUpdated: (Bool) -> Void
private let openTerms: () -> Void
private var checkNode: InteractiveCheckNode?
private let textNode: ImmediateTextNode
init(isAcceptedUpdated: @escaping (Bool) -> Void, openTerms: @escaping () -> Void) {
self.isAcceptedUpdated = isAcceptedUpdated
self.openTerms = openTerms
self.textNode = ImmediateTextNode()
self.textNode.maximumNumberOfLines = 0
super.init()
self.textNode.highlightAttributeAction = { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
} else {
return nil
}
}
self.textNode.tapAttributeAction = { [weak self] attributes, _ in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
self?.openTerms()
}
}
self.addSubnode(self.textNode)
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
guard let checkNode = self.checkNode else {
return
}
if case .ended = recognizer.state {
checkNode.setSelected(!checkNode.selected, animated: true)
checkNode.valueChanged?(checkNode.selected)
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.bounds.contains(point) {
return nil
}
if let (_, attributes) = self.textNode.attributesAtPoint(self.view.convert(point, to: self.textNode.view)) {
if attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] == nil {
return self.view
}
}
return super.hitTest(point, with: event)
}
func update(presentationData: PresentationData, botName: String, width: CGFloat, sideInset: CGFloat) -> CGFloat {
let spacing: CGFloat = 16.0
let topInset: CGFloat = 8.0
let checkNode: InteractiveCheckNode
if let current = self.checkNode {
checkNode = current
} else {
checkNode = InteractiveCheckNode(theme: CheckNodeTheme(backgroundColor: presentationData.theme.list.itemCheckColors.fillColor, strokeColor: presentationData.theme.list.itemCheckColors.foregroundColor, borderColor: presentationData.theme.list.itemCheckColors.strokeColor, overlayBorder: false, hasInset: false, hasShadow: false))
checkNode.valueChanged = { [weak self] value in
self?.isAcceptedUpdated(value)
}
self.checkNode = checkNode
self.addSubnode(checkNode)
}
let checkSize = CGSize(width: 22.0, height: 22.0)
self.textNode.linkHighlightColor = presentationData.theme.list.itemAccentColor.withAlphaComponent(0.3)
let attributedText = parseMarkdownIntoAttributedString(
presentationData.strings.Bot_AccepRecurrentInfo(botName).string,
attributes: MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: presentationData.theme.list.freeTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: presentationData.theme.list.freeTextColor),
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: presentationData.theme.list.itemAccentColor),
linkAttribute: { contents in
return (TelegramTextAttributes.URL, contents)
}
)
)
self.textNode.attributedText = attributedText
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0 - spacing - checkSize.width, height: .greatestFiniteMagnitude))
let height = textSize.height + 15.0
let contentWidth = checkSize.width + spacing + textSize.width
let contentOriginX = sideInset + floor((width - sideInset * 2.0 - contentWidth) / 2.0)
checkNode.frame = CGRect(origin: CGPoint(x: contentOriginX, y: topInset + floor((height - checkSize.height) / 2.0)), size: checkSize)
self.textNode.frame = CGRect(origin: CGPoint(x: contentOriginX + checkSize.width + spacing, y: topInset + floor((height - textSize.height) / 2.0)), size: textSize)
return height
}
}
private final class ActionButtonPanelNode: ASDisplayNode {
private(set) var isAccepted: Bool = false
var isAcceptedUpdated: (() -> Void)?
var openRecurrentTerms: (() -> Void)?
private var recurrentConfirmationNode: RecurrentConfirmationNode?
func update(presentationData: PresentationData, layout: ContainerViewLayout, invoice: BotPaymentInvoice?, botName: String?) -> (CGFloat, CGFloat) {
let bottomPanelVerticalInset: CGFloat = 16.0
var height = max(layout.intrinsicInsets.bottom, layout.inputHeight ?? 0.0) + bottomPanelVerticalInset * 2.0 + BotCheckoutActionButton.height
var actionButtonOffset: CGFloat = bottomPanelVerticalInset
if let invoice = invoice, let recurrentInfo = invoice.recurrentInfo, let botName = botName {
let recurrentConfirmationNode: RecurrentConfirmationNode
if let current = self.recurrentConfirmationNode {
recurrentConfirmationNode = current
} else {
recurrentConfirmationNode = RecurrentConfirmationNode(isAcceptedUpdated: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.isAccepted = value
strongSelf.isAcceptedUpdated?()
}, openTerms: { [weak self] in
self?.openRecurrentTerms?()
})
self.recurrentConfirmationNode = recurrentConfirmationNode
self.addSubnode(recurrentConfirmationNode)
}
let _ = recurrentInfo
let recurrentConfirmationHeight = recurrentConfirmationNode.update(presentationData: presentationData, botName: botName, width: layout.size.width, sideInset: layout.safeInsets.left + 33.0)
recurrentConfirmationNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: recurrentConfirmationHeight))
actionButtonOffset += recurrentConfirmationHeight
} else if let recurrentConfirmationNode = self.recurrentConfirmationNode {
self.recurrentConfirmationNode = nil
recurrentConfirmationNode.removeFromSupernode()
}
height += actionButtonOffset - bottomPanelVerticalInset
return (height, actionButtonOffset)
}
}
final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthorizationViewControllerDelegate {
private weak var controller: BotCheckoutController?
private let navigationBar: NavigationBar
@ -509,6 +666,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
private let paymentFormAndInfo = Promise<(BotPaymentForm, BotPaymentRequestedInfo, BotPaymentValidatedFormInfo?, String?, BotCheckoutPaymentMethod?, Int64?)?>(nil)
private var paymentFormValue: BotPaymentForm?
private var botPeerValue: EnginePeer?
private var currentFormInfo: BotPaymentRequestedInfo?
private var currentValidatedFormInfo: BotPaymentValidatedFormInfo?
private var currentShippingOptionId: String?
@ -516,7 +674,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
private var currentTipAmount: Int64?
private var formRequestDisposable: Disposable?
private let actionButtonPanelNode: ASDisplayNode
private let actionButtonPanelNode: ActionButtonPanelNode
private let actionButtonPanelSeparator: ASDisplayNode
private let actionButton: BotCheckoutActionButton
private let inProgressDimNode: ASDisplayNode
@ -585,13 +743,11 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
return (ItemListPresentationData(presentationData), (nodeState, arguments))
}
self.actionButtonPanelNode = ASDisplayNode()
self.actionButtonPanelNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
self.actionButtonPanelNode = ActionButtonPanelNode()
self.actionButtonPanelSeparator = ASDisplayNode()
self.actionButtonPanelSeparator.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
self.actionButton = BotCheckoutActionButton(activeFillColor: self.presentationData.theme.list.itemAccentColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
self.actionButton = BotCheckoutActionButton(activeFillColor: self.presentationData.theme.list.itemAccentColor, inactiveFillColor: self.presentationData.theme.list.itemDisabledTextColor.mixedWith(self.presentationData.theme.list.blocksBackgroundColor, alpha: 0.7), foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
self.actionButton.setState(.placeholder)
self.inProgressDimNode = ASDisplayNode()
@ -603,6 +759,22 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
self.arguments = arguments
self.actionButtonPanelNode.isAcceptedUpdated = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.updateActionButton()
}
self.actionButtonPanelNode.openRecurrentTerms = { [weak self] in
guard let strongSelf = self, let paymentForm = strongSelf.paymentFormValue, let recurrentInfo = paymentForm.invoice.recurrentInfo else {
return
}
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: recurrentInfo.termsUrl, forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {
self?.view.endEditing(true)
})
}
openInfoImpl = { [weak self] focus in
if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let currentFormInfo = strongSelf.currentFormInfo {
strongSelf.controller?.view.endEditing(true)
@ -924,6 +1096,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
savedInfo = BotPaymentRequestedInfo(name: nil, phone: nil, email: nil, shippingAddress: nil)
}
strongSelf.paymentFormValue = formAndValidatedInfo.form
strongSelf.botPeerValue = formAndValidatedInfo.botPeer
strongSelf.currentFormInfo = savedInfo
strongSelf.currentValidatedFormInfo = formAndValidatedInfo.validatedFormInfo
if let savedCredentials = formAndValidatedInfo.form.savedCredentials {
@ -959,6 +1132,38 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
}
}
})
self.actionButtonPanelSeparator.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
self.actionButtonPanelNode.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
self.visibleBottomContentOffsetChanged = { [weak self] offset in
guard let strongSelf = self else {
return
}
let panelColor: UIColor
let separatorColor: UIColor
switch offset {
case let .known(value):
if value > 10.0 {
panelColor = strongSelf.presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
separatorColor = strongSelf.presentationData.theme.rootController.navigationBar.separatorColor
} else {
panelColor = .clear
separatorColor = .clear
}
default:
panelColor = strongSelf.presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
separatorColor = strongSelf.presentationData.theme.rootController.navigationBar.separatorColor
}
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .linear)
if strongSelf.actionButtonPanelNode.backgroundColor != panelColor {
transition.updateBackgroundColor(node: strongSelf.actionButtonPanelNode, color: panelColor)
}
if strongSelf.actionButtonPanelSeparator.backgroundColor != separatorColor {
transition.updateBackgroundColor(node: strongSelf.actionButtonPanelSeparator, color: separatorColor)
}
}
}
deinit {
@ -971,22 +1176,34 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
private func updateActionButton() {
let totalAmount = currentTotalPrice(paymentForm: self.paymentFormValue, validatedFormInfo: self.currentValidatedFormInfo, currentShippingOptionId: self.currentShippingOptionId, currentTip: self.currentTipAmount)
let payString: String
var isButtonEnabled = true
if let paymentForm = self.paymentFormValue, totalAmount > 0 {
payString = self.presentationData.strings.Checkout_PayPrice(formatCurrencyAmount(totalAmount, currency: paymentForm.invoice.currency)).string
if let _ = paymentForm.invoice.recurrentInfo {
if !self.actionButtonPanelNode.isAccepted {
isButtonEnabled = false
}
}
} else {
payString = self.presentationData.strings.CheckoutInfo_Pay
}
self.actionButton.isEnabled = isButtonEnabled
if let currentPaymentMethod = self.currentPaymentMethod {
switch currentPaymentMethod {
case .applePay:
self.actionButton.setState(.applePay)
self.actionButton.setState(.applePay(isEnabled: isButtonEnabled))
default:
self.actionButton.setState(.active(payString))
self.actionButton.setState(.active(text: payString, isEnabled: isButtonEnabled))
}
} else {
self.actionButton.setState(.active(payString))
self.actionButton.setState(.active(text: payString, isEnabled: isButtonEnabled))
}
self.actionButtonPanelNode.isHidden = false
self.controller?.requestLayout(transition: .immediate)
}
private func updateIsInProgress(_ value: Bool) {
@ -1006,13 +1223,18 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
var updatedInsets = layout.intrinsicInsets
let bottomPanelHorizontalInset: CGFloat = 16.0
let bottomPanelVerticalInset: CGFloat = 16.0
let bottomPanelHeight = max(updatedInsets.bottom, layout.inputHeight ?? 0.0) + bottomPanelVerticalInset * 2.0 + BotCheckoutActionButton.height
var botName: String?
if let botPeer = self.botPeerValue {
botName = botPeer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)
}
let (bottomPanelHeight, actionButtonOffset) = self.actionButtonPanelNode.update(presentationData: self.presentationData, layout: layout, invoice: self.paymentFormValue?.invoice, botName: botName)
transition.updateFrame(node: self.actionButtonPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomPanelHeight), size: CGSize(width: layout.size.width, height: bottomPanelHeight)))
transition.updateFrame(node: self.actionButtonPanelSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
let actionButtonFrame = CGRect(origin: CGPoint(x: bottomPanelHorizontalInset, y: bottomPanelVerticalInset), size: CGSize(width: layout.size.width - bottomPanelHorizontalInset * 2.0, height: BotCheckoutActionButton.height))
let actionButtonFrame = CGRect(origin: CGPoint(x: bottomPanelHorizontalInset, y: actionButtonOffset), size: CGSize(width: layout.size.width - bottomPanelHorizontalInset * 2.0, height: BotCheckoutActionButton.height))
transition.updateFrame(node: self.actionButton, frame: actionButtonFrame)
self.actionButton.updateLayout(absoluteRect: actionButtonFrame.offsetBy(dx: self.actionButtonPanelNode.frame.minX, dy: self.actionButtonPanelNode.frame.minY), containerSize: layout.size, transition: transition)

View File

@ -305,8 +305,8 @@ final class BotReceiptControllerNode: ItemListControllerNode {
self.actionButtonPanelSeparator = ASDisplayNode()
self.actionButtonPanelSeparator.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
self.actionButton = BotCheckoutActionButton(activeFillColor: self.presentationData.theme.list.itemAccentColor, foregroundColor: self.presentationData.theme.list.plainBackgroundColor)
self.actionButton.setState(.active(self.presentationData.strings.Common_Done))
self.actionButton = BotCheckoutActionButton(activeFillColor: self.presentationData.theme.list.itemAccentColor, inactiveFillColor: self.presentationData.theme.list.itemDisabledTextColor, foregroundColor: self.presentationData.theme.list.plainBackgroundColor)
self.actionButton.setState(.active(text: self.presentationData.strings.Common_Done, isEnabled: true))
super.init(controller: controller, navigationBar: navigationBar, state: signal)

View File

@ -44,12 +44,17 @@ public struct BotPaymentInvoice : Equatable {
public var max: Int64
public var suggested: [Int64]
}
public struct RecurrentInfo: Equatable {
public var termsUrl: String
}
public let isTest: Bool
public let requestedFields: BotPaymentInvoiceFields
public let currency: String
public let prices: [BotPaymentPrice]
public let tip: Tip?
public let recurrentInfo: RecurrentInfo?
}
public struct BotPaymentNativeProvider : Equatable {
@ -124,39 +129,43 @@ public enum BotPaymentFormRequestError {
extension BotPaymentInvoice {
init(apiInvoice: Api.Invoice) {
switch apiInvoice {
case let .invoice(flags, currency, prices, maxTipAmount, suggestedTipAmounts, _):
var fields = BotPaymentInvoiceFields()
if (flags & (1 << 1)) != 0 {
fields.insert(.name)
case let .invoice(flags, currency, prices, maxTipAmount, suggestedTipAmounts, recurrentTermsUrl):
var fields = BotPaymentInvoiceFields()
if (flags & (1 << 1)) != 0 {
fields.insert(.name)
}
if (flags & (1 << 2)) != 0 {
fields.insert(.phone)
}
if (flags & (1 << 3)) != 0 {
fields.insert(.email)
}
if (flags & (1 << 4)) != 0 {
fields.insert(.shippingAddress)
}
if (flags & (1 << 5)) != 0 {
fields.insert(.flexibleShipping)
}
if (flags & (1 << 6)) != 0 {
fields.insert(.phoneAvailableToProvider)
}
if (flags & (1 << 7)) != 0 {
fields.insert(.emailAvailableToProvider)
}
var recurrentInfo: BotPaymentInvoice.RecurrentInfo?
if let recurrentTermsUrl = recurrentTermsUrl {
recurrentInfo = BotPaymentInvoice.RecurrentInfo(termsUrl: recurrentTermsUrl)
}
var parsedTip: BotPaymentInvoice.Tip?
if let maxTipAmount = maxTipAmount, let suggestedTipAmounts = suggestedTipAmounts {
parsedTip = BotPaymentInvoice.Tip(max: maxTipAmount, suggested: suggestedTipAmounts)
}
self.init(isTest: (flags & (1 << 0)) != 0, requestedFields: fields, currency: currency, prices: prices.map {
switch $0 {
case let .labeledPrice(label, amount):
return BotPaymentPrice(label: label, amount: amount)
}
if (flags & (1 << 2)) != 0 {
fields.insert(.phone)
}
if (flags & (1 << 3)) != 0 {
fields.insert(.email)
}
if (flags & (1 << 4)) != 0 {
fields.insert(.shippingAddress)
}
if (flags & (1 << 5)) != 0 {
fields.insert(.flexibleShipping)
}
if (flags & (1 << 6)) != 0 {
fields.insert(.phoneAvailableToProvider)
}
if (flags & (1 << 7)) != 0 {
fields.insert(.emailAvailableToProvider)
}
var parsedTip: BotPaymentInvoice.Tip?
if let maxTipAmount = maxTipAmount, let suggestedTipAmounts = suggestedTipAmounts {
parsedTip = BotPaymentInvoice.Tip(max: maxTipAmount, suggested: suggestedTipAmounts)
}
self.init(isTest: (flags & (1 << 0)) != 0, requestedFields: fields, currency: currency, prices: prices.map {
switch $0 {
case let .labeledPrice(label, amount):
return BotPaymentPrice(label: label, amount: amount)
}
}, tip: parsedTip)
}, tip: parsedTip, recurrentInfo: recurrentInfo)
}
}
}