Skip to content

Commit 90b540e

Browse files
committed
Evervault testing
Evervault testing
1 parent 9df4d94 commit 90b540e

3 files changed

Lines changed: 216 additions & 1 deletion

File tree

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@
101101
<artifactId>killbill-platform-osgi-bundles-lib-killbill</artifactId>
102102
<scope>provided</scope>
103103
</dependency>
104+
<dependency>
105+
<groupId>org.kill-bill.billing.plugin</groupId>
106+
<artifactId>killbill-plugin-api-control</artifactId>
107+
<scope>provided</scope>
108+
</dependency>
104109
<dependency>
105110
<groupId>org.kill-bill.billing.plugin</groupId>
106111
<artifactId>killbill-plugin-api-invoice</artifactId>

src/main/java/org/killbill/billing/plugin/helloworld/HelloWorldActivator.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.osgi.framework.BundleContext;
4040
import org.osgi.util.tracker.ServiceTracker;
4141
import org.killbill.billing.invoice.plugin.api.InvoiceFormatterFactory;
42+
import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
4243

4344
public class HelloWorldActivator extends KillbillActivatorBase {
4445

@@ -78,6 +79,9 @@ public void start(final BundleContext context) throws Exception {
7879
// changed to any other plugin api)
7980
final PaymentPluginApi paymentPluginApi = new HelloWorldPaymentPluginApi();
8081
registerPaymentPluginApi(context, paymentPluginApi);
82+
83+
final PaymentControlPluginApi paymentControlPluginApi = new HelloWorldPaymentControlPluginApi();
84+
registerPaymentControlPluginApi(context, paymentControlPluginApi);
8185

8286
// Expose metrics (optional)
8387
metricsGenerator = new MetricsGeneratorExample(metricRegistry);
@@ -133,10 +137,16 @@ private void registerInvoicePluginApi(final BundleContext context, final Invoice
133137
props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, PLUGIN_NAME);
134138
registrar.registerService(context, InvoicePluginApi.class, api, props);
135139
}
140+
141+
private void registerPaymentControlPluginApi(final BundleContext context, final PaymentControlPluginApi api) {
142+
final Hashtable<String, String> props = new Hashtable<String, String>();
143+
props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, PLUGIN_NAME);
144+
registrar.registerService(context, PaymentControlPluginApi.class, api, props);
145+
}
136146

137147
private void registerHealthcheck(final BundleContext context, final Healthcheck healthcheck) {
138148
final Hashtable<String, String> props = new Hashtable<String, String>();
139149
props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, PLUGIN_NAME);
140150
registrar.registerService(context, Healthcheck.class, healthcheck, props);
141151
}
142-
}
152+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* POC: Simulates the Evervault control plugin for Kill Bill.
3+
*
4+
* This plugin implements PaymentControlPluginApi ONLY.
5+
* It intercepts two moments:
6+
*
7+
* 1. addPaymentMethodWithControl — stores the encrypted PAN (ev:... token)
8+
* in an in-memory map, then returns adjustedPluginName to re-tag
9+
* the payment method under the target PSP plugin (e.g. killbill-stripe).
10+
*
11+
* 2. purchasePayment / authorizePayment — retrieves the stored PAN
12+
* and injects it as ccNumber in the plugin properties so the
13+
* PSP plugin can use it.
14+
*
15+
* In production, the in-memory map would be replaced by a database table.
16+
*/
17+
18+
package org.killbill.billing.plugin.helloworld;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.UUID;
23+
import java.util.concurrent.ConcurrentHashMap;
24+
25+
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
26+
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
27+
import org.killbill.billing.control.plugin.api.PaymentControlApiException;
28+
import org.killbill.billing.control.plugin.api.PaymentControlContext;
29+
import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
30+
import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
31+
import org.killbill.billing.payment.api.PluginProperty;
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
35+
public class HelloWorldPaymentControlPluginApi implements PaymentControlPluginApi {
36+
37+
private static final Logger logger = LoggerFactory.getLogger(HelloWorldPaymentControlPluginApi.class);
38+
39+
// In-memory storage: accountId -> stored card data
40+
// In production, this would be a database table keyed by paymentMethodId
41+
private final ConcurrentHashMap<UUID, StoredCardData> cardStore = new ConcurrentHashMap<>();
42+
43+
private static class StoredCardData {
44+
final String encPan;
45+
final String encCvv;
46+
final String expirationMonth;
47+
final String expirationYear;
48+
final String targetPlugin;
49+
50+
StoredCardData(String encPan, String encCvv, String expirationMonth,
51+
String expirationYear, String targetPlugin) {
52+
this.encPan = encPan;
53+
this.encCvv = encCvv;
54+
this.expirationMonth = expirationMonth;
55+
this.expirationYear = expirationYear;
56+
this.targetPlugin = targetPlugin;
57+
}
58+
}
59+
60+
@Override
61+
public PriorPaymentControlResult priorCall(final PaymentControlContext context,
62+
final Iterable<PluginProperty> properties)
63+
throws PaymentControlApiException {
64+
65+
final String encPan = getPluginProperty(properties, "encPan");
66+
67+
if (encPan != null) {
68+
return handleAddPaymentMethod(context, properties, encPan);
69+
} else {
70+
return handlePayment(context, properties);
71+
}
72+
}
73+
74+
private PriorPaymentControlResult handleAddPaymentMethod(
75+
final PaymentControlContext context,
76+
final Iterable<PluginProperty> properties,
77+
final String encPan) {
78+
79+
final String encCvv = getPluginProperty(properties, "encCvv");
80+
final String expMonth = getPluginProperty(properties, "expirationMonth");
81+
final String expYear = getPluginProperty(properties, "expirationYear");
82+
final String targetPlugin = getPluginProperty(properties, "targetPlugin");
83+
84+
final UUID accountId = context.getAccountId();
85+
86+
final StoredCardData card = new StoredCardData(encPan, encCvv, expMonth, expYear, targetPlugin);
87+
cardStore.put(accountId, card);
88+
89+
logger.info("[Evervault POC] Stored encrypted PAN for accountId={}. Returning adjustedPluginName={}", accountId, targetPlugin);
90+
91+
return new PriorPaymentControlResult() {
92+
@Override
93+
public boolean isAborted() { return false; }
94+
95+
@Override
96+
public java.math.BigDecimal getAdjustedAmount() { return null; }
97+
98+
@Override
99+
public org.killbill.billing.catalog.api.Currency getAdjustedCurrency() { return null; }
100+
101+
@Override
102+
public UUID getAdjustedPaymentMethodId() { return null; }
103+
104+
@Override
105+
public Iterable<PluginProperty> getAdjustedPluginProperties() { return properties; }
106+
107+
@Override
108+
public String getAdjustedPluginName() { return targetPlugin; }
109+
};
110+
}
111+
112+
private PriorPaymentControlResult handlePayment(
113+
final PaymentControlContext context,
114+
final Iterable<PluginProperty> properties) {
115+
116+
final UUID accountId = context.getAccountId();
117+
final StoredCardData card = cardStore.get(accountId);
118+
119+
if (card == null) {
120+
logger.info("[Evervault POC] No stored card for accountId={}. Passing through.", accountId);
121+
return new DefaultPriorPaymentControlResult(properties);
122+
}
123+
124+
logger.info("[Evervault POC] Injecting encrypted PAN for accountId={}. TransactionType={}", accountId, context.getTransactionType());
125+
126+
final List<PluginProperty> adjustedProperties = new ArrayList<>();
127+
for (PluginProperty prop : properties) {
128+
adjustedProperties.add(prop);
129+
}
130+
adjustedProperties.add(new PluginProperty("ccNumber", card.encPan, false));
131+
adjustedProperties.add(new PluginProperty("ccExpirationMonth", card.expirationMonth, false));
132+
adjustedProperties.add(new PluginProperty("ccExpirationYear", card.expirationYear, false));
133+
if (card.encCvv != null) {
134+
adjustedProperties.add(new PluginProperty("ccVerificationValue", card.encCvv, false));
135+
}
136+
137+
return new DefaultPriorPaymentControlResult(adjustedProperties);
138+
}
139+
140+
@Override
141+
public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext context,
142+
final Iterable<PluginProperty> properties)
143+
throws PaymentControlApiException {
144+
logger.info("[Evervault POC] onSuccessCall for accountId={}", context.getAccountId());
145+
return new OnSuccessPaymentControlResult() {
146+
@Override
147+
public Iterable<PluginProperty> getAdjustedPluginProperties() { return properties; }
148+
};
149+
}
150+
151+
@Override
152+
public OnFailurePaymentControlResult onFailureCall(final PaymentControlContext context,
153+
final Iterable<PluginProperty> properties)
154+
throws PaymentControlApiException {
155+
logger.info("[Evervault POC] onFailureCall for accountId={}", context.getAccountId());
156+
return new OnFailurePaymentControlResult() {
157+
@Override
158+
public org.joda.time.DateTime getNextRetryDate() { return null; }
159+
160+
@Override
161+
public Iterable<PluginProperty> getAdjustedPluginProperties() { return properties; }
162+
};
163+
}
164+
165+
private String getPluginProperty(final Iterable<PluginProperty> properties, final String key) {
166+
if (properties == null) return null;
167+
for (PluginProperty prop : properties) {
168+
if (key.equals(prop.getKey())) {
169+
return prop.getValue() != null ? prop.getValue().toString() : null;
170+
}
171+
}
172+
return null;
173+
}
174+
175+
private static class DefaultPriorPaymentControlResult implements PriorPaymentControlResult {
176+
private final Iterable<PluginProperty> properties;
177+
178+
DefaultPriorPaymentControlResult(Iterable<PluginProperty> properties) {
179+
this.properties = properties;
180+
}
181+
182+
@Override
183+
public boolean isAborted() { return false; }
184+
185+
@Override
186+
public java.math.BigDecimal getAdjustedAmount() { return null; }
187+
188+
@Override
189+
public org.killbill.billing.catalog.api.Currency getAdjustedCurrency() { return null; }
190+
191+
@Override
192+
public UUID getAdjustedPaymentMethodId() { return null; }
193+
194+
@Override
195+
public Iterable<PluginProperty> getAdjustedPluginProperties() { return properties; }
196+
197+
@Override
198+
public String getAdjustedPluginName() { return null; }
199+
}
200+
}

0 commit comments

Comments
 (0)