summaryrefslogtreecommitdiff
path: root/simulation/sim-terminal.go
blob: a639fc8028ef1ef65de2a86f11a2816086d47915 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package main

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

const TERMINAL_PROVIDER = "Simulation"

const TERMINAL_ID = "1"

// retrieved from the cli tool when added the terminal
const TERMINAL_USER_ID = TERMINAL_PROVIDER + "-" + TERMINAL_ID

// retrieved from the cli tool when added the terminal
const TERMINAL_ACCESS_TOKEN = "secret"

const SIM_TERMINAL_LONG_POLL_MS_STR = "20000" // 20 seconds

const QR_CODE_CONTENT_BASE = "taler://withdraw/localhost:8082/c2ec/"

func Terminal(in chan *SimulatedPhysicalInteraction, out chan *SimulatedPhysicalInteraction, kill chan error) {

	fmt.Println("TERMINAL: Terminal idle... awaiting readiness message of sim-wallet")
	<-in

	fmt.Println("TERMINAL: Sim-Wallet ready, generating WOPID...	")
	wopidBytes := make([]byte, 32)
	_, err := rand.Read(wopidBytes)
	if err != nil {
		fmt.Println("TERMINAL: failed creating the wopid:", err.Error(), "(ends simulation)")
		kill <- err
	}

	wopid := FormatWopid(wopidBytes)
	fmt.Println("TERMINAL: Generated Nonce (base64 url encoded):", wopid)
	uri := QR_CODE_CONTENT_BASE + wopid
	fmt.Println("TERMINAL: Taler Withdrawal URI:", uri)

	// note for realworld implementation
	// -> start long polling always before showing the QR code
	awaitSelection := make(chan *C2ECWithdrawalStatus)
	longPollFailed := make(chan error)

	fmt.Println("TERMINAL: now sending long poll request to c2ec from terminal and await parameter selection")
	go func() {

		url := FormatUrl(
			C2EC_BANK_WITHDRAWAL_STATUS_URL,
			map[string]string{"wopid": wopid},
			map[string]string{"long_poll_ms": SIM_TERMINAL_LONG_POLL_MS_STR},
		)
		response, status, err := HttpGet(
			url,
			map[string]string{"Authorization": TerminalAuth()},
			NewJsonCodec[C2ECWithdrawalStatus](),
		)
		if err != nil {
			kill <- err
			return
		}
		if status != 200 {
			longPollFailed <- errors.New("status of withdrawal status response was " + strconv.Itoa(status))
			return
		}

		awaitSelection <- response
	}()

	fmt.Println("Go is too fast :) ... need to sleep a bit that long polling request is guaranteed to be executed before the POST of the registration. This won't be a problem in real world appliance.")
	time.Sleep(time.Duration(10) * time.Millisecond)

	if !DISABLE_DELAYS {
		fmt.Println("TERMINAL: simulating QR Code scan. delay:", WALLET_SCAN_QR_CODE_DELAY_MS)
		time.Sleep(time.Duration(WALLET_SCAN_QR_CODE_DELAY_MS) * time.Millisecond)
	} else {
		fmt.Println("TERMINAL: simulating QR Code scan.")
	}
	out <- &SimulatedPhysicalInteraction{Msg: uri}
	for {
		select {
		case w := <-awaitSelection:
			fmt.Println("TERMINAL: parameters selected:", w.ReservePubKey)
			if !DISABLE_DELAYS {
				fmt.Println("TERMINAL: simulating user interaction. customer presents card. delay:", TERMINAL_ACCEPT_CARD_DELAY_MS)
				time.Sleep(time.Duration(TERMINAL_ACCEPT_CARD_DELAY_MS) * time.Millisecond)
			} else {
				fmt.Println("TERMINAL: simulating user interaction. customer presents card.")
			}
			if !DISABLE_DELAYS {
				fmt.Println("TERMINAL: card accepted. terminal waits for response of provider backend. delay:", PROVIDER_BACKEND_PAYMENT_DELAY_MS)
				time.Sleep(time.Duration(PROVIDER_BACKEND_PAYMENT_DELAY_MS) * time.Millisecond)
			} else {
				fmt.Println("TERMINAL: card accepted. terminal waits for response of provider backend.")
			}

			terminalId, err := strconv.Atoi(TERMINAL_ID)
			if err != nil {
				fmt.Println("failed parsing the terminal id.")
				kill <- err
			}

			fmt.Println("TERMINAL: payment was processed at the provider backend. sending payment notification.")
			paymentNotification := &C2ECPaymentNotification{
				ProviderTransactionId: "simulation-transaction-id-0",
				TerminalId:            terminalId,
				Amount: Amount{
					Currency: "CHF",
					Fraction: 10,
					Value:    10,
				},
				Fees: Amount{
					Currency: "CHF",
					Fraction: 10,
					Value:    0,
				},
			}
			cdc := NewJsonCodec[C2ECPaymentNotification]()
			pnbytes, err := cdc.EncodeToBytes(paymentNotification)
			if err != nil {
				fmt.Println("TERMINAL: failed serializing payment notification")
				kill <- err
			}
			paymentUrl := FormatUrl(
				C2EC_BANK_WITHDRAWAL_PAYMENT_URL,
				map[string]string{"wopid": wopid},
				map[string]string{},
			)
			_, err = http.Post(
				paymentUrl,
				cdc.HttpApplicationContentHeader(),
				bytes.NewReader(pnbytes),
			)
			if err != nil {
				fmt.Println("TERMINAL: error on POST request:", err.Error())
				kill <- err
			}
			fmt.Println("TERMINAL: Terminal flow ended")
		case f := <-longPollFailed:
			fmt.Println("TERMINAL: long-polling for selection failed... error:", err.Error())
			kill <- f
		}
	}
}

func TerminalAuth() string {

	userAndPw := fmt.Sprintf("%s:%s", TERMINAL_USER_ID, TERMINAL_ACCESS_TOKEN)
	return base64.StdEncoding.EncodeToString([]byte(userAndPw))
}