Skip to content

Commit 5ab9cb1

Browse files
authored
Merge pull request #851 from alphagov/PP-6758-csv-consumer-uses-flat-refunds
PP-6758 CSV consumer uses flat refunds
2 parents 1d4775c + e328172 commit 5ab9cb1

5 files changed

Lines changed: 93 additions & 62 deletions

File tree

src/main/java/uk/gov/pay/ledger/transaction/dao/TransactionDao.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public class TransactionDao {
7171
"ORDER BY t.created_date DESC OFFSET :offset LIMIT :limit";
7272

7373
private static final String SEARCH_TRANSACTIONS_CURSOR =
74-
SEARCH_TRANSACTIONS_WITH_PARENT_BASE +
74+
"SELECT * FROM transaction t " +
7575
":searchExtraFields " +
7676
":cursorFields " +
7777
"ORDER BY t.created_date DESC, t.id DESC LIMIT :limit";
@@ -295,7 +295,7 @@ public List<TransactionEntity> cursorTransactionSearch(TransactionSearchParams s
295295
query.bind("startingAfterId", startingAfterId);
296296
query.bind("limit", cursorPageSize);
297297

298-
return query.map(new TransactionWithParentMapper()).list();
298+
return query.map(new TransactionMapper()).list();
299299
});
300300
}
301301

src/main/java/uk/gov/pay/ledger/transaction/model/CsvTransactionFactory.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import static java.math.BigDecimal.valueOf;
2626
import static org.apache.commons.lang3.StringUtils.isBlank;
27+
import static org.apache.commons.lang3.StringUtils.lowerCase;
2728
import static org.apache.commons.lang3.StringUtils.replaceChars;
2829
import static org.apache.commons.text.WordUtils.capitalizeFully;
2930
import static uk.gov.pay.ledger.util.JsonParser.safeGetAsLong;
@@ -81,29 +82,30 @@ public Map<String, Object> toMap(TransactionEntity transactionEntity) {
8182

8283
if (TransactionType.PAYMENT.toString().equals(transactionEntity.getTransactionType())) {
8384
result.putAll(
84-
getPaymentTransactionAttributes(transactionEntity)
85+
getPaymentTransactionAttributes(transactionEntity, transactionDetails)
8586
);
8687

88+
result.put(FIELD_GOVUK_PAYMENT_ID, transactionEntity.getExternalId());
8789
result.put(FIELD_AMOUNT, penceToCurrency(transactionEntity.getAmount()));
8890
result.put(FIELD_TOTAL_AMOUNT, penceToCurrency(totalAmount));
8991
result.put(FIELD_NET, penceToCurrency(netAmount));
9092
result.put(FIELD_FEE, penceToCurrency(transactionEntity.getFee()));
9193
result.put(FIELD_STATE, PaymentState.getDisplayName(transactionEntity.getState()));
94+
result.put(FIELD_MOTO, transactionEntity.isMoto());
9295
}
9396
if (TransactionType.REFUND.toString().equals(transactionEntity.getTransactionType())) {
94-
if (transactionEntity.getParentTransactionEntity() != null) {
95-
result.putAll(
96-
getPaymentTransactionAttributes(transactionEntity.getParentTransactionEntity())
97-
);
98-
}
99-
97+
result.putAll(
98+
getPaymentTransactionAttributes(transactionEntity, transactionDetails.get("payment_details"))
99+
);
100+
result.put(FIELD_GOVUK_PAYMENT_ID, transactionEntity.getParentExternalId());
100101
result.put(FIELD_AMOUNT, penceToCurrency(transactionEntity.getAmount() * -1));
101102
result.put(FIELD_NET, penceToCurrency(netAmount * -1));
102103
result.put(FIELD_TOTAL_AMOUNT, penceToCurrency(totalAmount * -1));
103104
result.put(FIELD_ISSUED_BY, safeGetAsString(transactionDetails, "user_email"));
104105
result.put(FIELD_STATE, RefundState.getDisplayName(transactionEntity.getState()));
105106
}
106107

108+
result.put(FIELD_PROVIDER_ID, sanitiseAgainstSpreadsheetFormulaInjection(transactionEntity.getGatewayTransactionId()));
107109
result.put(FIELD_DATE_CREATED, dateCreated);
108110
result.put(FIELD_TIME_CREATED, timeCreated);
109111
result.put(FIELD_CORPORATE_CARD_SURCHARGE, penceToCurrency(
@@ -130,28 +132,24 @@ public Map<String, Object> toMap(TransactionEntity transactionEntity) {
130132
return result;
131133
}
132134

133-
private Map<String, Object> getPaymentTransactionAttributes(TransactionEntity transactionEntity) throws IOException {
134-
Map<String, Object> result = new HashMap<>();
135+
private Map<String, Object> getPaymentTransactionAttributes(TransactionEntity transactionEntity, JsonNode details) {
135136

136-
JsonNode transactionDetails = objectMapper.readTree(
137-
Optional.ofNullable(transactionEntity.getTransactionDetails()).orElse("{}"));
137+
Map<String, Object> result = new HashMap<>();
138138

139139
result.put(FIELD_REFERENCE, sanitiseAgainstSpreadsheetFormulaInjection(transactionEntity.getReference()));
140140
result.put(FIELD_DESC, sanitiseAgainstSpreadsheetFormulaInjection(transactionEntity.getDescription()));
141141
result.put(FIELD_EMAIL, sanitiseAgainstSpreadsheetFormulaInjection(transactionEntity.getEmail()));
142142
result.put(FIELD_CARDHOLDER_NAME, sanitiseAgainstSpreadsheetFormulaInjection(transactionEntity.getCardholderName()));
143143
result.put(FIELD_CARD_NUMBER, transactionEntity.getLastDigitsCardNumber());
144-
result.put(FIELD_GOVUK_PAYMENT_ID, transactionEntity.getExternalId());
145-
146-
result.put(FIELD_CARD_BRAND, safeGetAsString(transactionDetails, "card_brand_label"));
147-
result.put(FIELD_CARD_EXPIRY_DATE, safeGetAsString(transactionDetails, "expiry_date"));
148-
result.put(FIELD_PROVIDER_ID, safeGetAsString(transactionDetails, "gateway_transaction_id"));
149-
result.put(FIELD_CARD_TYPE, StringUtils.lowerCase(safeGetAsString(transactionDetails, "card_type")));
150-
result.put(FIELD_MOTO, transactionEntity.isMoto());
151144

152-
result.put(FIELD_WALLET_TYPE, capitalizeFully(
153-
replaceChars(safeGetAsString(transactionDetails, "wallet"), '_', ' '))
154-
);
145+
if (details != null) {
146+
result.put(FIELD_CARD_BRAND, safeGetAsString(details, "card_brand_label"));
147+
result.put(FIELD_CARD_EXPIRY_DATE, safeGetAsString(details, "expiry_date"));
148+
result.put(FIELD_CARD_TYPE, lowerCase(safeGetAsString(details, "card_type")));
149+
result.put(FIELD_WALLET_TYPE, capitalizeFully(
150+
replaceChars(safeGetAsString(details, "wallet"), '_', ' '))
151+
);
152+
}
155153

156154
return result;
157155
}

src/test/java/uk/gov/pay/ledger/transaction/model/CsvTransactionFactoryTest.java

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,14 @@ public void setUp() {
3333
.withState(TransactionState.FAILED_REJECTED)
3434
.withTransactionType(TransactionType.PAYMENT.name())
3535
.withAmount(100L)
36+
.withGatewayTransactionId("gateway-transaction-id")
3637
.withCreatedDate(ZonedDateTime.parse("2018-03-12T16:25:01.123456Z"))
3738
.withTotalAmount(123L)
38-
.withTransactionDetails(
39-
new GsonBuilder().create()
40-
.toJson(ImmutableMap.builder()
41-
.put("expiry_date", "10/24")
42-
.put("user_email", "refundedbyuser@example.org")
43-
.put("corporate_surcharge", 23)
44-
.put("wallet", "APPLE_PAY")
45-
.put("card_type", "DEBIT")
46-
.put("card_brand_label", "Visa")
47-
.build())
48-
);
39+
.withMoto(true)
40+
.withCorporateCardSurcharge(23)
41+
.withCardBrandLabel("Visa")
42+
.withDefaultCardDetails()
43+
.withDefaultTransactionDetails();
4944
}
5045

5146
@Test
@@ -58,6 +53,8 @@ public void toMapShouldReturnMapWithCorrectCsvDataForPaymentTransaction() {
5853
assertPaymentDetails(csvDataMap, transactionEntity);
5954

6055
assertThat(csvDataMap.get("Amount"), is("1.00"));
56+
assertThat(csvDataMap.get("GOV.UK Payment ID"), is(transactionEntity.getExternalId()));
57+
assertThat(csvDataMap.get("Provider ID"), is(transactionEntity.getGatewayTransactionId()));
6158
assertThat(csvDataMap.get("State"), is("Declined"));
6259
assertThat(csvDataMap.get("Finished"), is(true));
6360
assertThat(csvDataMap.get("Error Code"), is("P0010"));
@@ -66,27 +63,30 @@ public void toMapShouldReturnMapWithCorrectCsvDataForPaymentTransaction() {
6663
assertThat(csvDataMap.get("Time Created"), is("16:25:01"));
6764
assertThat(csvDataMap.get("Corporate Card Surcharge"), is("0.23"));
6865
assertThat(csvDataMap.get("Total Amount"), is("1.23"));
66+
assertThat(csvDataMap.get("MOTO"), is(true));
6967
}
7068

7169
@Test
7270
public void toMapShouldReturnMapWithCorrectCsvDataForRefundTransaction() {
7371

74-
TransactionEntity transactionEntity = transactionFixture
75-
.withMoto(true)
76-
.toEntity();
77-
TransactionEntity refundTransactionEntity = transactionFixture.withTransactionType("REFUND")
72+
TransactionFixture refundTransactionFixture = aTransactionFixture()
73+
.withCreatedDate(ZonedDateTime.parse("2018-03-12T16:25:01.123456Z"))
74+
.withTransactionType("REFUND")
7875
.withAmount(99L)
7976
.withTotalAmount(99L)
8077
.withState(TransactionState.ERROR)
81-
.withParentTransactionEntity(transactionEntity)
82-
.withTransactionDetails(new GsonBuilder().create()
83-
.toJson(ImmutableMap.builder().put("user_email", "refundedbyuser@example.org").build()))
84-
.toEntity();
78+
.withGatewayTransactionId("refund-gateway-transaction-id")
79+
.withRefundedByUserEmail("refundedbyuser@example.org")
80+
.withParentExternalId("parent-external-id")
81+
.withDefaultPaymentDetails()
82+
.withDefaultTransactionDetails();
8583

86-
Map<String, Object> csvDataMap = csvTransactionFactory.toMap(refundTransactionEntity);
84+
Map<String, Object> csvDataMap = csvTransactionFactory.toMap(refundTransactionFixture.toEntity());
8785

88-
assertPaymentDetails(csvDataMap, refundTransactionEntity.getParentTransactionEntity());
86+
assertPaymentDetails(csvDataMap, refundTransactionFixture.toEntity());
8987
assertThat(csvDataMap.get("Amount"), is("-0.99"));
88+
assertThat(csvDataMap.get("Provider ID"), is(refundTransactionFixture.getGatewayTransactionId()));
89+
assertThat(csvDataMap.get("GOV.UK Payment ID"), is(refundTransactionFixture.getParentExternalId()));
9090
assertThat(csvDataMap.get("State"), is("Refund error"));
9191
assertThat(csvDataMap.get("Finished"), is(true));
9292
assertThat(csvDataMap.get("Error Code"), is("P0050"));
@@ -96,7 +96,7 @@ public void toMapShouldReturnMapWithCorrectCsvDataForRefundTransaction() {
9696
assertThat(csvDataMap.get("Corporate Card Surcharge"), is("0.00"));
9797
assertThat(csvDataMap.get("Total Amount"), is("-0.99"));
9898
assertThat(csvDataMap.get("Net"), is("-0.99"));
99-
assertThat(csvDataMap.get("MOTO"), is(true));
99+
assertThat(csvDataMap.get("MOTO"), is(nullValue()));
100100
}
101101

102102
@Test
@@ -206,11 +206,9 @@ private void assertPaymentDetails(Map<String, Object> csvDataMap, TransactionEnt
206206
assertThat(csvDataMap.get("Email"), is(transactionEntity.getEmail()));
207207
assertThat(csvDataMap.get("Card Brand"), is("Visa"));
208208
assertThat(csvDataMap.get("Cardholder Name"), is(transactionEntity.getCardholderName()));
209-
assertThat(csvDataMap.get("Card Expiry Date"), is("10/24"));
209+
assertThat(csvDataMap.get("Card Expiry Date"), is("10/21"));
210210
assertThat(csvDataMap.get("Card Number"), is(transactionEntity.getLastDigitsCardNumber()));
211-
assertThat(csvDataMap.get("Provider ID"), is(transactionEntity.getGatewayTransactionId()));
212-
assertThat(csvDataMap.get("GOV.UK Payment ID"), is(transactionEntity.getExternalId()));
213-
assertThat(csvDataMap.get("Card Type"), is("debit"));
211+
assertThat(csvDataMap.get("Card Type"), is("credit"));
214212
assertThat(csvDataMap.get("Wallet Type"), is("Apple Pay"));
215213
}
216214
}

src/test/java/uk/gov/pay/ledger/transaction/resource/TransactionResourceCsvIT.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package uk.gov.pay.ledger.transaction.resource;
22

33
import com.google.common.collect.ImmutableMap;
4-
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
54
import org.apache.commons.csv.CSVParser;
65
import org.apache.commons.csv.CSVRecord;
76
import org.junit.jupiter.api.BeforeEach;
87
import org.junit.jupiter.api.Test;
9-
import org.junit.jupiter.api.extension.ExtendWith;
108
import org.junit.jupiter.api.extension.RegisterExtension;
119
import uk.gov.pay.ledger.extension.AppWithPostgresAndSqsExtension;
1210
import uk.gov.pay.ledger.metadatakey.dao.MetadataKeyDao;
@@ -75,7 +73,7 @@ public void shouldGetAllTransactionsAsCSVWithAcceptType() throws IOException {
7573
.withGatewayAccountId(otherGatewayAccountId)
7674
.insert(rule.getJdbi());
7775

78-
aTransactionFixture()
76+
TransactionFixture refundTransactionFixture = aTransactionFixture()
7977
.withTransactionType("REFUND")
8078
.withParentExternalId(transactionFixture.getExternalId())
8179
.withGatewayAccountId(transactionFixture.getGatewayAccountId())
@@ -84,6 +82,8 @@ public void shouldGetAllTransactionsAsCSVWithAcceptType() throws IOException {
8482
.withAmount(100L)
8583
.withTotalAmount(100L)
8684
.withState(TransactionState.ERROR_GATEWAY)
85+
.withGatewayTransactionId("refund-gateway-transaction-id")
86+
.withDefaultPaymentDetails()
8787
.withDefaultTransactionDetails()
8888
.insert(rule.getJdbi());
8989

@@ -110,8 +110,10 @@ public void shouldGetAllTransactionsAsCSVWithAcceptType() throws IOException {
110110

111111
CSVRecord paymentRecord = csvRecords.get(0);
112112
assertThat(paymentRecord.size(), is(22));
113-
assertPaymentTransactionDetails(paymentRecord, transactionFixture);
113+
assertCommonPaymentFields(paymentRecord, transactionFixture);
114114
assertThat(paymentRecord.get("Amount"), is("1.23"));
115+
assertThat(paymentRecord.get("GOV.UK Payment ID"), is(transactionFixture.getExternalId()));
116+
assertThat(paymentRecord.get("Provider ID"), is(transactionFixture.getGatewayTransactionId()));
115117
assertThat(paymentRecord.get("State"), is("Error"));
116118
assertThat(paymentRecord.get("Finished"), is("true"));
117119
assertThat(paymentRecord.get("Error Code"), is("P0050"));
@@ -127,8 +129,10 @@ public void shouldGetAllTransactionsAsCSVWithAcceptType() throws IOException {
127129
assertThat(paymentRecord.isMapped("MOTO"), is(false));
128130

129131
CSVRecord refundRecord = csvRecords.get(1);
130-
assertPaymentTransactionDetails(refundRecord, transactionFixture);
132+
assertCommonPaymentFields(refundRecord, refundTransactionFixture);
131133
assertThat(refundRecord.get("Amount"), is("-1.00"));
134+
assertThat(refundRecord.get("GOV.UK Payment ID"), is(refundTransactionFixture.getParentExternalId()));
135+
assertThat(refundRecord.get("Provider ID"), is(refundTransactionFixture.getGatewayTransactionId()));
132136
assertThat(refundRecord.get("State"), is("Refund error"));
133137
assertThat(refundRecord.get("Finished"), is("true"));
134138
assertThat(refundRecord.get("Error Code"), is("P0050"));
@@ -139,6 +143,7 @@ public void shouldGetAllTransactionsAsCSVWithAcceptType() throws IOException {
139143
assertThat(refundRecord.get("Total Amount"), is("-1.00"));
140144
assertThat(refundRecord.get("Wallet Type"), is("Apple Pay"));
141145
assertThat(refundRecord.get("Issued By"), is("refund-by-user-email@example.org"));
146+
assertThat(refundRecord.isMapped("MOTO"), is(false));
142147
}
143148

144149
@Test
@@ -364,16 +369,14 @@ public void shouldGetAllTransactionsForAGivenGatewayPayoutId() throws IOExceptio
364369
assertThat(paymentRecord.get("GOV.UK Payment ID"), is(transactionFixture.getExternalId()));
365370
}
366371

367-
private void assertPaymentTransactionDetails(CSVRecord csvRecord, TransactionFixture transactionFixture) {
372+
private void assertCommonPaymentFields(CSVRecord csvRecord, TransactionFixture transactionFixture) {
368373
assertThat(csvRecord.get("Reference"), is(transactionFixture.getReference()));
369374
assertThat(csvRecord.get("Description"), is(transactionFixture.getDescription()));
370375
assertThat(csvRecord.get("Email"), is("someone@example.org"));
371-
assertThat(csvRecord.get("Card Brand"), is("Diners Club"));
372-
assertThat(csvRecord.get("Cardholder Name"), is("J Doe"));
376+
assertThat(csvRecord.get("Card Brand"), is(transactionFixture.getCardBrandLabel()));
377+
assertThat(csvRecord.get("Cardholder Name"), is(transactionFixture.getCardholderName()));
373378
assertThat(csvRecord.get("Card Expiry Date"), is("10/21"));
374379
assertThat(csvRecord.get("Card Number"), is("1234"));
375-
assertThat(csvRecord.get("Provider ID"), is("gateway-transaction-id"));
376-
assertThat(csvRecord.get("GOV.UK Payment ID"), is(transactionFixture.getExternalId()));
377380
assertThat(csvRecord.get("Card Type"), is("credit"));
378381
}
379382
}

src/test/java/uk/gov/pay/ledger/util/fixture/TransactionFixture.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ public Long getId() {
120120
return id;
121121
}
122122

123+
public String getCardExpiryDate() {
124+
return cardExpiryDate;
125+
}
126+
123127
public TransactionFixture withId(Long id) {
124128
this.id = id;
125129
return this;
@@ -221,12 +225,18 @@ public String getCardBrand() {
221225
return cardBrand;
222226
}
223227

228+
public String getCardBrandLabel() {
229+
return cardBrandLabel;
230+
}
231+
224232
public TransactionFixture withCardBrand(String cardBrand) {
225233
this.cardBrand = cardBrand;
226234
return this;
227235
}
228236

229-
public boolean isMoto() { return moto;}
237+
public boolean isMoto() {
238+
return moto;
239+
}
230240

231241
public TransactionFixture withMoto(boolean moto) {
232242
this.moto = moto;
@@ -378,6 +388,10 @@ public TransactionFixture insert(Jdbi jdbi) {
378388
@NotNull
379389
private JsonObject getTransactionDetail() {
380390
JsonObject transactionDetails = new JsonObject();
391+
JsonObject refundPaymentDetails = new JsonObject();
392+
393+
String defaultCardType = String.valueOf(CREDIT);
394+
String defaultWalletType = "APPLE_PAY";
381395

382396
transactionDetails.addProperty("language", language);
383397
transactionDetails.addProperty("return_url", returnUrl);
@@ -387,8 +401,17 @@ private JsonObject getTransactionDetail() {
387401
transactionDetails.addProperty("corporate_surcharge", corporateCardSurcharge);
388402
transactionDetails.addProperty("refunded_by", refundedById);
389403
transactionDetails.addProperty("user_email", refundedByUserEmail);
390-
transactionDetails.addProperty("card_type", String.valueOf(CREDIT));
391-
transactionDetails.addProperty("wallet", "APPLE_PAY");
404+
transactionDetails.addProperty("card_type", defaultCardType);
405+
transactionDetails.addProperty("wallet", defaultWalletType);
406+
407+
if ("REFUND".equals(transactionType)) {
408+
refundPaymentDetails.addProperty("card_brand_label", cardBrandLabel);
409+
refundPaymentDetails.addProperty("expiry_date", cardExpiryDate);
410+
refundPaymentDetails.addProperty("card_type", defaultCardType);
411+
refundPaymentDetails.addProperty("wallet", defaultWalletType);
412+
transactionDetails.add("payment_details", refundPaymentDetails);
413+
}
414+
392415
Optional.ofNullable(cardBrandLabel)
393416
.ifPresent(cardBrandLabel -> transactionDetails.addProperty("card_brand_label", cardBrandLabel));
394417
Optional.ofNullable(externalMetadata)
@@ -593,4 +616,13 @@ public TransactionFixture withRefundedByUserEmail(String refundedByUserEmail) {
593616
this.refundedByUserEmail = refundedByUserEmail;
594617
return this;
595618
}
619+
620+
public TransactionFixture withDefaultPaymentDetails() {
621+
// default values are already assigned for cardBrand, cardholderName, reference, description, email
622+
623+
this.firstDigitsCardNumber = "123456";
624+
this.lastDigitsCardNumber = "1234";
625+
this.cardBrandLabel = "Visa";
626+
return this;
627+
}
596628
}

0 commit comments

Comments
 (0)