Skip to content

Commit

Permalink
[PAYOP-1116] Refine network token module interface (#330)
Browse files Browse the repository at this point in the history
* Combine module

* Remove raw value parameters

* Rename tests

* Fix doc
  • Loading branch information
fangpenlin authored Mar 22, 2023
1 parent 42a83fb commit b790c86
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import net.starlark.java.eval.StarlarkInt;
import net.starlark.java.eval.StarlarkThread;

@StarlarkBuiltin(name = "nts", category = "BUILTIN", doc = "Overridable Network Token API in Larky")
@StarlarkBuiltin(name = "native_nts", category = "BUILTIN", doc = "Overridable Network Token API in Larky")
public class NetworkTokenModule implements LarkyNetworkToken {
public static final NetworkTokenModule INSTANCE = new NetworkTokenModule();

Expand Down
79 changes: 79 additions & 0 deletions larky/src/main/resources/vgs/nts.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
load("@vgs//native_nts", _nts="native_nts")
load("@stdlib//larky", larky="larky")
load("@vendor//jsonpath_ng", jsonpath_ng="jsonpath_ng")


def render(
input,
pan,
cvv,
amount,
currency_code,
exp_month=None,
exp_year=None,
cryptogram_value=None,
cryptogram_eci=None,
):
"""Retrieves a network token for the given PAN alias, renders the cryptogram, and injects the network token values
into the payload.
For the output JSONPaths, please note that inserting a value into a non-existing deep nested note is not currently
supported. For example, for an input payload like this::
input = {
"data": {}
}
To insert into `$.data.network_token.exp_month` JSONPath, you need to place any value at the exact path first
like this in order to make JSONPath value insertion work::
input["data"]["network_token"] = {"exp_month": "TO_BE_REPLACED"}
nts.render(input, ...)
:param input: JSON payload to inject network token into
:param pan: JSONPath to the PAN alias in the input payload. Used to look up the corresponding network token to be
rendered and injected into the payload.
:param cvv: JSONPath to the CVV of the credit card in the input payload. Used to pass to the network for retrieving
the corresponding network token and cryptogram to be returned.
:param amount: JSONPath to the amount of payment for the transaction to be made with the network token in the input
payload. Used to pass to the network for retrieving the corresponding network token and cryptogram to be
returned.
:param currency_code: JSONPath to the currency code of payment amount for the transaction to be made with the
network token in the input payload. Used to pass to the network for retrieving the corresponding network
token and cryptogram to be returned.
:param exp_month: JSONPath to insert the expiration month of the network token within the input payload
:param exp_year: JSONPath to insert the expiration year of the network token within the input payload
:param cryptogram_value: JSONPath to insert the cryptogram value of the network token within the input
payload
:param cryptogram_eci: JSONPath to insert the cryptogram ECI of the network token within the input payload
:return: JSON payload injected with network token values
"""
pan_value = jsonpath_ng.parse(pan).find(input).value
cvv_value = jsonpath_ng.parse(cvv).find(input).value
amount_value = str(jsonpath_ng.parse(amount).find(input).value)
currency_code_value = jsonpath_ng.parse(currency_code).find(input).value

network_token = _nts.get_network_token(
pan=pan_value,
cvv=cvv_value,
amount=amount_value,
currency_code=currency_code_value,
)
placements = [
(pan, network_token["token"]),
(exp_month, network_token["exp_month"]),
(exp_year, network_token["exp_year"]),
(cryptogram_value, network_token["cryptogram_value"]),
(cryptogram_eci, network_token["cryptogram_eci"]),
]
for path, value in placements:
if path == None:
continue
input = jsonpath_ng.parse(path).update(input, value).value
return input


nts = larky.struct(
get_network_token=_nts.get_network_token,
render=render,
)
104 changes: 0 additions & 104 deletions larky/src/main/resources/vgs/nts_helpers.star

This file was deleted.

20 changes: 0 additions & 20 deletions larky/src/main/resources/vgs/vault.star

This file was deleted.

90 changes: 84 additions & 6 deletions larky/src/test/resources/vgs_tests/nts/test_default_nts.star
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,36 @@ load("@stdlib//unittest", "unittest")
load("@vgs//nts", "nts")


def _make_fixture():
return {
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"reference": "YOUR_PAYMENT_REFERENCE",
"amount": {
"currency": "USD",
"value": 1000,
},
"paymentMethod": {
"type": "networkToken",
"holderName": "CARDHOLDER_NAME",
"number": "785840aLpH4nUmV9985",
"expiryMonth": "TO_BE_REPLACED",
"expiryYear": "TO_BE_REPLACED",
"cvv": "123",
},
"returnUrl": "https://your-company.com/",
"shopperReference": "YOUR_SHOPPER_REFERENCE",
"recurringProcessingModel": "CardOnFile",
"shopperInteraction": "Ecommerce",
# Deep JSONPath is not supported by the JSONPath lib, so that we need to
# create an empty object here manually.
# ref: https://github.com/json-path/JsonPath/issues/83
"mpiData": {
"cavv": "TO_BE_REPLACED",
"eci": "TO_BE_REPLACED",
}
}


def _test_get_network_token():
output = nts.get_network_token(
pan="MOCK_PAN_ALIAS",
Expand All @@ -19,12 +49,12 @@ def _test_get_network_token():
})


def _test_pan_empty_value():
def _test_get_network_token_pan_empty_value():
asserts.assert_fails(lambda: nts.get_network_token("", cvv="MOCK_CVV", amount="123.45", currency_code="USD"),
"pan argument cannot be blank")


def _test_not_found():
def _test_get_network_token_not_found():
input = {
"pan": "NOT_FOUND",
}
Expand All @@ -33,14 +63,62 @@ def _test_not_found():
"network token is not found")


def _test_render():
output = nts.render(
_make_fixture(),
pan="$.paymentMethod.number",
cvv="$.paymentMethod.cvv",
amount="$.amount.value",
currency_code="$.amount.currency",
exp_month="$.paymentMethod.expiryMonth",
exp_year="$.paymentMethod.expiryYear",
cryptogram_value="$.mpiData.cavv",
cryptogram_eci="$.mpiData.eci",
)
asserts.assert_that(output["paymentMethod"]["number"]).is_equal_to("4242424242424242")
asserts.assert_that(output["paymentMethod"]["expiryMonth"]).is_equal_to(12)
asserts.assert_that(output["paymentMethod"]["expiryYear"]).is_equal_to(27)
asserts.assert_that(output["mpiData"]["cavv"]).is_equal_to("MOCK_CRYPTOGRAM_VALUE")
asserts.assert_that(output["mpiData"]["eci"]).is_equal_to("MOCK_CRYPTOGRAM_ECI")


def _test_render_pan_empty_value():
asserts.assert_fails(
lambda: nts.render(
{"pan": "", "cvv": "MOCK_CVV", "amount": "MOCK_AMOUNT", "currency_code": "MOCK_CURRENCY_CODE"},
pan="$.pan",
cvv="$.cvv",
amount="$.amount",
currency_code="$.currency_code",
),
"pan argument cannot be blank",
)


def _test_render_not_found():
asserts.assert_fails(
lambda: nts.render(
{"pan": "NOT_FOUND", "cvv": "MOCK_CVV", "amount": "MOCK_AMOUNT", "currency_code": "MOCK_CURRENCY_CODE"},
pan="$.pan",
cvv="$.cvv",
amount="$.amount",
currency_code="$.currency_code",
),
"network token is not found",
)


def _suite():
_suite = unittest.TestSuite()

# Redact Tests
# Get network token tests
_suite.addTest(unittest.FunctionTestCase(_test_get_network_token))
_suite.addTest(unittest.FunctionTestCase(_test_pan_empty_value))
_suite.addTest(unittest.FunctionTestCase(_test_not_found))

_suite.addTest(unittest.FunctionTestCase(_test_get_network_token_pan_empty_value))
_suite.addTest(unittest.FunctionTestCase(_test_get_network_token_not_found))
# Render tests
_suite.addTest(unittest.FunctionTestCase(_test_render))
_suite.addTest(unittest.FunctionTestCase(_test_render_pan_empty_value))
_suite.addTest(unittest.FunctionTestCase(_test_render_not_found))
return _suite


Expand Down
Loading

0 comments on commit b790c86

Please sign in to comment.