Skip to content

Commit 6fea609

Browse files
authored
Merge pull request #11 from fiskaltrust/10-tip-handling
#10 added docs about how payment vendor side tip is handled + extend …
2 parents d55c7bd + 13bafb1 commit 6fea609

6 files changed

Lines changed: 64 additions & 24 deletions

File tree

HOWTO_01_Payment_csharp/Program.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ static async Task Main(string[] args)
6363
// - the terminal ID is not defined here (null) so the request will be processed by all available terminals for the cashbox
6464
// IMPORTANT: In a real setup you might want to define a specific terminal ID here to target a specific payment terminal device; especially when multiple payment terminals are registered for the same cashbox!
6565
// - we provide the operation ID to be able to retry in case of failure
66-
ExecutedResult<PayResponse> payResult = await ftPosAPI.Pay.PaymentAsync(new PayItemRequest
66+
var payItemRequest = new PayItemRequest
6767
{
6868
Description = "Card",
6969
Amount = amount,
70-
}, fiskaltrust.Payment.DTO.PaymentProtocol.use_auto, null, operationId);
70+
};
71+
ExecutedResult<PayResponse> payResult = await ftPosAPI.Pay.PaymentAsync(payItemRequest, fiskaltrust.Payment.DTO.PaymentProtocol.use_auto, null, operationId);
7172

7273
/////////////////////////////////////////////////////////////////////////////////////////////////
7374
// Check Result
@@ -80,7 +81,7 @@ static async Task Main(string[] args)
8081
{
8182
// YES --> SUCCESS: Payment was successful
8283
PayResponse payResp = await payResult.Operation.GetResponseAsAsync();
83-
Utils.DumpToLogger(payResp);
84+
Utils.DumpToLogger(payResp, payItemRequest);
8485
break;
8586
}
8687
else

HOWTO_01_Payment_csharp/README.MD

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,22 @@ Several critical error scenarios must be managed carefully to prevent double pay
2525
- The response is not received within the expected time frame (HTTP timeout), which can be simulated by a device losing internet connectivity during payment execution.
2626

2727
In all of these cases, the solution is to resend the original request using the identical operation ID and request body. The backend will then return the final result of the operation.
28+
29+
## Special case - payment vendor added TIP
30+
31+
In many payment vendor apps, it is possible to add a tip to the payment amount.
32+
**Example flow in a restaurant:**
33+
1. The guest asks for the bill.
34+
2. The waiter triggers to create the bill on the POS (e.g., mobile device).
35+
3. The POS on the mobile device sends the amount (e.g., 10€) to be paid to the configured payment app via POS System API and the **fiskaltrust** InStore App.
36+
4. The payment app opens, and the waiter hands the mobile device to the guest.
37+
5. The guest sees a TIP entry screen and adds a tip of 2€.
38+
6. The guest now pays the full sum of 12€.
39+
7. The payment app reports a paid amount of 12€ (including 2€ tip).
40+
8. The **fiskaltrust** InStore App / POS System API reports the following in the payment response (see also an example on the POS System API docs):
41+
- 2 pay items
42+
- Pay item 1: The total paid amount with the receipt -> 12€
43+
- Pay item 2: The tip with a negative amount -> -2€
44+
- The sum of the 2 pay items is the original requested amount of 10€
45+
46+
**NOTE:** The tip can be calculated by simply subtracting the requested amount from the paid amount (12-10 = 2€ tip) or alternatively by using the 2nd pay item (-2 * -1 = 2€ tip).

HOWTO_08_pay_sign_issue_csharp/Program.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@ static async Task Main(string[] args)
5454
decimal totalAmount = chargeItems.Sum(ci => ci.Amount);
5555
Logger.LogInfo($"Total amount to pay: {totalAmount} EUR");
5656

57+
PayItemRequest payRequest = new()
58+
{
59+
Amount = totalAmount,
60+
Description = "Card"
61+
};
5762
var payRunner = new ftPosAPIOperationRunner();
5863
(PayResponse? pResp, string errorMsg) = await payRunner.Execute<PayResponse>(async () =>
5964
{
60-
PayItemRequest payRequest = new()
61-
{
62-
Amount = totalAmount,
63-
Description = "Card"
64-
};
65+
6566
return await ftPosAPI.Pay.PaymentAsync(payRequest, PaymentProtocol.use_auto, null, payRunner.OperationID);
6667
});
6768

@@ -72,7 +73,7 @@ static async Task Main(string[] args)
7273
else
7374
{
7475
Logger.LogInfo("Payment succeeded.");
75-
Utils.DumpToLogger(pResp);
76+
Utils.DumpToLogger(pResp, payRequest);
7677

7778
///////////////////////////////////////////////////////////////////////////////////////////////////////
7879
///

README.MD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Any payment amount will return a SUCCESS response, except for the following defi
8080
| 30000,10 | DECLINED |
8181
| 30000,20 | TIMEOUT (returned as an error message as no other option is available yet) |
8282
| 30000,40 | CANCELLED BY USER |
83-
| 30000,50 | SUCCESS with added guest tip |
83+
| 30000,50 | SUCCESS with added guest tip (see [HOWTO_01_Payment](HOWTO_01_Payment_csharp/README.MD) for instructions on handling tips) |
8484
| 30000,60 | SUCCESS after 1-minute delay |
8585
| 30000,70 | SUCCESS after 3-minute delay |
8686
| 30000,80 | SUCCESS after 6-minute delay |

libPosSystemAPI.Test/IntegrationTestsPayment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public async Task TestPayment()
3939
Assert.NotNull(payResponse);
4040
Assert.True(payResponse.Protocol == Payment.DTO.PaymentProtocol.use_auto);
4141
Assert.NotNull(payResponse.ftPayItems);
42-
// dummy payment provider should return exactly one pay item (as we did not execute a 30000,50 which would result in multiple pay items as there would be a tip)
42+
// dummy payment provider should return exactly one pay item
4343
Assert.Single(payResponse.ftPayItems);
4444

4545
PayItem pi = payResponse!.ftPayItems![0];

libPosSystemAPI/PosAPIUtils/Utils.cs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public static (Guid ftCashboxID, string ftCashboxAccessToken)? GetCashboxCredent
182182
return (ftCashboxID, ftCashboxAccessToken);
183183
}
184184

185-
public static void DumpToLogger(PayResponse payResp)
185+
public static void DumpToLogger(PayResponse payResp, PayItemRequest? payItemRequest = null)
186186
{
187187
Logger.LogInfo("Payment successful! Queue ID: " + payResp.ftQueueID);
188188
if (payResp.ftPayItems == null || payResp.ftPayItems.Length == 0)
@@ -193,9 +193,15 @@ public static void DumpToLogger(PayResponse payResp)
193193

194194
// pretty log the response (JSON)
195195
Logger.LogDebug("PayResponse: " + JsonSerializer.Serialize(payResp, new JsonSerializerOptions { WriteIndented = true }));
196-
197-
foreach (PayItem ftPayItem in payResp.ftPayItems)
196+
if (payItemRequest != null)
197+
{
198+
Logger.LogInfo("Requested Payment:");
199+
Logger.LogInfo($"- Amount: {payItemRequest.Amount}");
200+
}
201+
Logger.LogInfo("Received Pay Response:");
202+
for (int i = 0; i < payResp.ftPayItems.Length; i++)
198203
{
204+
PayItem ftPayItem = payResp.ftPayItems[i];
199205
var payItemCaseData = ftPayItem.GetPayItemCaseData();
200206

201207
Dictionary<string, JsonElement>? providerInfo = payItemCaseData?.Provider;
@@ -204,19 +210,32 @@ public static void DumpToLogger(PayResponse payResp)
204210
{
205211
protocol = protocolValue.GetString() ?? "ERROR: invalid type";
206212
}
207-
Logger.LogInfo($"- {protocol}:");
208-
if (payItemCaseData?.Receipt == null)
209-
{
210-
Logger.LogInfo("\t WARNING: No receipt info received!");
211-
}
212-
else
213+
Logger.LogInfo($"- PayItem {i+1}");
214+
Logger.LogInfo($"\t\tDescription: {ftPayItem.Description}");
215+
if (protocol != "unknown") Logger.LogInfo($"\t\tprotocol: {protocol}");
216+
Logger.LogInfo($"\t\tAmount: {ftPayItem.Amount}");
217+
// only show calculate included tip and process receipt for the real payment item and not for the additional info entries
218+
if (payItemCaseData != null)
213219
{
214-
string[]? payReceipt = ftPayItem.GetPayItemCaseData()?.Receipt;
215-
if (payReceipt != null)
220+
if (payItemRequest != null)
221+
{
222+
decimal tipAmount = ftPayItem.Amount - payItemRequest.Amount;
223+
Logger.LogInfo($"\t\t\tIncluded Tip Amount: {tipAmount}");
224+
}
225+
Logger.LogInfo("\t\tReceipt:");
226+
if (payItemCaseData?.Receipt == null)
227+
{
228+
Logger.LogInfo("\t\t\t WARNING: No receipt info received!");
229+
}
230+
else
216231
{
217-
foreach (string line in payReceipt)
232+
string[]? payReceipt = ftPayItem.GetPayItemCaseData()?.Receipt;
233+
if (payReceipt != null)
218234
{
219-
Logger.LogInfo($"\t{line}");
235+
foreach (string line in payReceipt)
236+
{
237+
Logger.LogInfo($"\t\t\t{line}");
238+
}
220239
}
221240
}
222241
}

0 commit comments

Comments
 (0)