• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Adyen / adyen-web / 9001078091

08 May 2024 11:31AM UTC coverage: 68.83%. First build
9001078091

Pull #2673

github

web-flow
Merge 58d188c81 into 33729afe1
Pull Request #2673: [waiting for translation]feat: add Upi intent flow

3253 of 5217 branches covered (62.35%)

Branch coverage included in aggregate %.

65 of 71 new or added lines in 10 files covered. (91.55%)

5412 of 7372 relevant lines covered (73.41%)

95.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.77
/packages/lib/src/components/UPI/UPI.tsx
1
import { h, RefObject } from 'preact';
2
import UIElement from '../UIElement';
3
import UPIComponent from './components/UPIComponent';
4
import CoreProvider from '../../core/Context/CoreProvider';
5
import Await from '../internal/Await';
6
import QRLoader from '../internal/QRLoader';
7
import { TX_VARIANT, UPIElementProps, UpiMode, UpiPaymentData } from './types';
8
import SRPanelProvider from '../../core/Errors/SRPanelProvider';
9
import isMobile from '../../utils/isMobile';
10
/**
11
 * For mobile:
12
 * We should show upi_collect or upi_intent depending on if `apps` are returned in /paymentMethods response
13
 * The upi_qr should always be on the second tab
14
 *
15
 * For non-mobile:
16
 * We should never show the upi_intent (ignore `apps` in /paymentMethods response)
17
 * The upi_qr should be on the first tab and the upi_collect should be on second tab
18
 */
19

20
class UPI extends UIElement<UPIElementProps> {
21
    public static type = 'upi';
14✔
22

23
    private selectedMode: UpiMode;
24

25
    constructor(props: UPIElementProps) {
26
        super(props);
16✔
27
        this.selectedMode = this.props.defaultMode;
16✔
28
    }
29

30
    formatProps(props: UPIElementProps) {
31
        if (!isMobile()) {
22✔
32
            return {
10✔
33
                ...super.formatProps(props),
34
                defaultMode: props?.defaultMode ?? UpiMode.QrCode,
16✔
35
                // For large screen, ignore the apps
36
                apps: []
37
            };
38
        }
39

40
        const hasIntentApps = props.apps?.length > 0;
12✔
41
        const fallbackDefaultMode = hasIntentApps ? UpiMode.Intent : UpiMode.Vpa;
12✔
42
        const allowedModes = [fallbackDefaultMode, UpiMode.QrCode];
12✔
43
        const upiCollectApp = {
12✔
44
            id: UpiMode.Vpa,
45
            name: props.i18n.get('upi.collect.dropdown.label'),
46
            type: TX_VARIANT.UpiCollect
47
        };
48
        const apps = hasIntentApps ? [...props.apps.map(app => ({ ...app, type: TX_VARIANT.UpiIntent })), upiCollectApp] : [];
12✔
49
        return {
12✔
50
            ...super.formatProps(props),
51
            defaultMode: allowedModes.includes(props?.defaultMode) ? props.defaultMode : fallbackDefaultMode,
12!
52
            apps
53
        };
54
    }
55

56
    public get isValid(): boolean {
57
        return this.state.isValid;
6✔
58
    }
59

60
    public formatData(): UpiPaymentData {
61
        if (this.selectedMode === UpiMode.QrCode) {
20✔
62
            return {
2✔
63
                paymentMethod: {
64
                    type: TX_VARIANT.UpiQr
65
                }
66
            };
67
        }
68

69
        const { virtualPaymentAddress, app } = this.state.data;
18✔
70
        const type = this.selectedMode === UpiMode.Vpa ? TX_VARIANT.UpiCollect : app?.type;
18✔
71
        return {
18✔
72
            paymentMethod: {
73
                ...(type && { type }),
36✔
74
                ...(type === TX_VARIANT.UpiCollect && virtualPaymentAddress && { virtualPaymentAddress }),
42✔
75
                ...(type === TX_VARIANT.UpiIntent && app?.id && { appId: app.id })
30✔
76
            }
77
        };
78
    }
79

80
    private onUpdateMode = (mode: UpiMode): void => {
16✔
NEW
81
        this.selectedMode = mode;
×
82
        if (mode === UpiMode.QrCode) {
×
83
            /**
84
             * When selecting QR code mode, we need to clear the state data and trigger the 'onChange'.
85
             */
86
            this.setState({ data: {}, valid: {}, errors: {}, isValid: true });
×
87
        }
88
    };
89

90
    private renderContent(type: string): h.JSX.Element {
91
        switch (type) {
2!
92
            case 'qrCode':
93
                return (
×
94
                    <QRLoader
95
                        ref={ref => {
96
                            this.componentRef = ref;
×
97
                        }}
98
                        {...this.props}
99
                        qrCodeData={this.props.qrCodeData ? encodeURIComponent(this.props.qrCodeData) : null}
×
100
                        type={TX_VARIANT.UpiQr}
101
                        brandLogo={this.props.brandLogo || this.icon}
×
102
                        onComplete={this.onComplete}
103
                        introduction={this.props.i18n.get('upi.qrCodeWaitingMessage')}
104
                        countdownTime={5}
105
                        onActionHandled={this.props.onActionHandled}
106
                    />
107
                );
108
            case 'await':
109
                return (
×
110
                    <Await
111
                        ref={ref => {
112
                            this.componentRef = ref;
×
113
                        }}
114
                        onError={this.props.onError}
115
                        clientKey={this.props.clientKey}
116
                        paymentData={this.props.paymentData}
117
                        onComplete={this.onComplete}
118
                        brandLogo={this.icon}
119
                        type={TX_VARIANT.UpiCollect}
120
                        messageText={this.props.i18n.get('upi.vpaWaitingMessage')}
121
                        awaitText={this.props.i18n.get('await.waitForConfirmation')}
122
                        showCountdownTimer
123
                        countdownTime={5}
124
                        onActionHandled={this.props.onActionHandled}
125
                    />
126
                );
127
            default:
128
                return (
2✔
129
                    <UPIComponent
130
                        ref={(ref: RefObject<typeof UPIComponent>) => {
131
                            this.componentRef = ref;
4✔
132
                        }}
133
                        payButton={this.payButton}
134
                        onChange={this.setState}
135
                        onUpdateMode={this.onUpdateMode}
136
                        apps={this.props.apps}
137
                        defaultMode={this.props.defaultMode}
138
                        showPayButton={this.props.showPayButton}
139
                    />
140
                );
141
        }
142
    }
143

144
    public render(): h.JSX.Element {
145
        const { type } = this.props;
2✔
146
        return (
2✔
147
            <CoreProvider i18n={this.props.i18n} loadingContext={this.props.loadingContext} resources={this.resources}>
148
                <SRPanelProvider srPanel={this.props.modules.srPanel}>{this.renderContent(type)}</SRPanelProvider>
149
            </CoreProvider>
150
        );
151
    }
152
}
153

154
export default UPI;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc