Init & create payment (not working)
This commit is contained in:
parent
9ea21c3276
commit
9775274d71
270
Cargo.lock
generated
270
Cargo.lock
generated
@ -25,7 +25,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -51,7 +51,7 @@ dependencies = [
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project-lite 0.2.9",
|
||||
"smallvec",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
@ -68,7 +68,7 @@ dependencies = [
|
||||
"log",
|
||||
"memchr",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
@ -140,7 +140,7 @@ checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000"
|
||||
dependencies = [
|
||||
"actix-macros",
|
||||
"futures-core",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -157,7 +157,7 @@ dependencies = [
|
||||
"mio 0.8.5",
|
||||
"num_cpus",
|
||||
"socket2 0.4.7",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@ -692,7 +692,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -734,7 +734,7 @@ dependencies = [
|
||||
"strum",
|
||||
"tarpc",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"whatlang",
|
||||
]
|
||||
@ -1099,9 +1099,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
|
||||
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@ -1111,9 +1111,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
|
||||
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@ -1126,15 +1126,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
|
||||
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
|
||||
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1178,9 +1178,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.3.2"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "database_manager"
|
||||
@ -1373,7 +1373,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -1570,9 +1570,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
|
||||
checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
@ -1710,7 +1710,7 @@ dependencies = [
|
||||
"rumqttc",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"uuid 1.2.2",
|
||||
]
|
||||
@ -2007,30 +2007,11 @@ dependencies = [
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-util 0.7.4",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2_wasi"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "756e9dfbaf90887b69ea8113ef9d04a9fec0ec4ac17b84a95006381f6f7966f9"
|
||||
dependencies = [
|
||||
"bytes 1.3.0",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio-util_wasi",
|
||||
"tokio_wasi",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "4.3.5"
|
||||
@ -2239,7 +2220,7 @@ dependencies = [
|
||||
"itoa 1.0.4",
|
||||
"pin-project-lite 0.2.9",
|
||||
"socket2 0.4.7",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
@ -2267,34 +2248,10 @@ dependencies = [
|
||||
"bytes 1.3.0",
|
||||
"hyper 0.14.23",
|
||||
"native-tls",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper_wasi"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b903a973bd98c326103ea94336a9ffe44c57c71517d6be7639438021053dc6e"
|
||||
dependencies = [
|
||||
"bytes 1.3.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2_wasi",
|
||||
"http",
|
||||
"http-body 0.4.5",
|
||||
"httparse",
|
||||
"httpdate 1.0.2",
|
||||
"itoa 1.0.4",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tokio_wasi",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
"wasmedge_wasi_socket",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.53"
|
||||
@ -2371,9 +2328,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.21.2"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "261bf85ed492cd1c47c9ba675e48649682a9d2d2e77f515c5386d7726fb0ba76"
|
||||
checksum = "197f4e300af8b23664d4077bf5c40e0afa9ba66a567bb5a51d3def3c7b287d1c"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
@ -2457,9 +2414,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.1"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
|
||||
checksum = "ec947b7a4ce12e3b87e353abae7ce124d025b6c7d6c5aea5cc0bcf92e9510ded"
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
@ -2614,9 +2571,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@ -2904,19 +2861,6 @@ dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio_wasi"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e260e68b7a4187e52caf00a035aaf7166391c2c2529123fe8229d4d8f86e15a2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasmedge_wasi_socket",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.2.2"
|
||||
@ -2994,9 +2938,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
@ -3095,22 +3039,11 @@ version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "openapi"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"reqwest 0.11.13",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.43"
|
||||
version = "0.10.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376"
|
||||
checksum = "29d971fd5722fec23977260f6e81aa67d2f22cadbdc2aa049f1022d9a3be1566"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
@ -3140,9 +3073,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.78"
|
||||
version = "0.9.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132"
|
||||
checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"cc",
|
||||
@ -3257,7 +3190,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -3344,21 +3277,6 @@ version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
|
||||
[[package]]
|
||||
name = "pay_u"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_more",
|
||||
"dotenv",
|
||||
"log",
|
||||
"reqwest_wasi",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "payment_adapter"
|
||||
version = "0.1.0"
|
||||
@ -3388,6 +3306,7 @@ dependencies = [
|
||||
"opentelemetry 0.17.0",
|
||||
"opentelemetry-jaeger",
|
||||
"payment_adapter",
|
||||
"reqwest 0.11.13",
|
||||
"rumqttc",
|
||||
"serde",
|
||||
"sqlx",
|
||||
@ -3395,7 +3314,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -3955,7 +3874,6 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
@ -3963,7 +3881,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"url",
|
||||
@ -3973,41 +3891,6 @@ dependencies = [
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest_wasi"
|
||||
version = "0.11.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c1c0524070b75dc4a924bdaac56d5c874205497fb3132f09f3270e4efb188d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes 1.3.0",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2_wasi",
|
||||
"http",
|
||||
"http-body 0.4.5",
|
||||
"hyper_wasi",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite 0.2.9",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio_wasi",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
@ -4084,7 +3967,7 @@ dependencies = [
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile 0.3.0",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
@ -4315,7 +4198,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -4758,7 +4641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
@ -4798,7 +4681,7 @@ dependencies = [
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"tracing-opentelemetry",
|
||||
"tracing-subscriber",
|
||||
@ -4940,7 +4823,7 @@ dependencies = [
|
||||
"static_assertions",
|
||||
"tarpc-plugins",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tokio-serde",
|
||||
"tokio-util 0.7.4",
|
||||
"tracing",
|
||||
@ -5153,7 +5036,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
"uuid 1.2.2",
|
||||
]
|
||||
@ -5178,9 +5061,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.22.0"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3"
|
||||
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"bytes 1.3.0",
|
||||
@ -5193,7 +5076,7 @@ dependencies = [
|
||||
"signal-hook-registry",
|
||||
"socket2 0.4.7",
|
||||
"tokio-macros",
|
||||
"winapi 0.3.9",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5214,7 +5097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5224,7 +5107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
@ -5252,7 +5135,7 @@ checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5290,42 +5173,10 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"pin-project-lite 0.2.9",
|
||||
"slab",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util_wasi"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4e7e921c33dc681c0b01aa4c5aa1bf619f46a7040a1565ddb1f84bc4666c1bd"
|
||||
dependencies = [
|
||||
"bytes 1.3.0",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tokio_wasi",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio_wasi"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68e9a04f238fa1676caa7c897bf1dbe7a82269b675aca14176a028af7a2167d2"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"bytes 1.3.0",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio_wasi",
|
||||
"num_cpus",
|
||||
"pin-project-lite 0.2.9",
|
||||
"socket2 0.4.7",
|
||||
"wasmedge_wasi_socket",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
@ -5474,9 +5325,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
@ -5676,7 +5527,7 @@ dependencies = [
|
||||
"log",
|
||||
"rusty_pool",
|
||||
"thiserror",
|
||||
"tokio 1.22.0",
|
||||
"tokio 1.23.0",
|
||||
"wapc",
|
||||
]
|
||||
|
||||
@ -5812,15 +5663,6 @@ dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmedge_wasi_socket"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fc4e93009d7e2c2e3d29b65d1f56ef606466673e85fe417808beca50cb608bf"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.93.0"
|
||||
|
@ -25,8 +25,8 @@ members = [
|
||||
# "crates/api",
|
||||
"crates/web",
|
||||
# vendor
|
||||
"vendor/t_pay",
|
||||
"vendor/pay_u",
|
||||
# "vendor/t_pay",
|
||||
# "vendor/pay_u",
|
||||
]
|
||||
exclude = [
|
||||
"crates/payment_adapter_pay_u",
|
||||
|
12
README.md
12
README.md
@ -20,3 +20,15 @@ This script will treat all crates which starts with `payment_adapter_` as a paym
|
||||
mkdir -p adapters
|
||||
./scripts/build-adapters
|
||||
```
|
||||
|
||||
##### `init`
|
||||
|
||||
| Argument | Result |
|
||||
|-----------------------------------------|--------------------------|
|
||||
| payment_argument::PaymentProviderConfig | payment_argument::Status |
|
||||
|
||||
* `create_payment`
|
||||
|
||||
| Argument | Result |
|
||||
|--------------------------------|-------------------------------|
|
||||
| payment_adapter::CreatePayment | payment_adapter::OrderCreated |
|
||||
|
@ -531,7 +531,7 @@ pub struct PaymentConfig {
|
||||
#[serde(default)]
|
||||
optional_payment: bool,
|
||||
#[serde(flatten)]
|
||||
providers: HashMap<String, PaymentProviderConfig>,
|
||||
pub providers: HashMap<String, PaymentProviderConfig>,
|
||||
}
|
||||
|
||||
impl Example for PaymentConfig {
|
||||
|
31
crates/payment_adapter/Readme.adoc
Normal file
31
crates/payment_adapter/Readme.adoc
Normal file
@ -0,0 +1,31 @@
|
||||
= Payment Adapter Implementation
|
||||
|
||||
== Extention Lifetime
|
||||
|
||||
Payment Adapter extension is loaded as WASI file and initialize as a module.
|
||||
Then to store it in runtime `name` function is called to load extension unique `name` (`String`).
|
||||
|
||||
Using this `name` extension configuration from application configuration, names must match! In config is missing is missing application will print error message is stop.
|
||||
|
||||
Then configuration is passed to extension to setup client. If everything goes as expected it should return `Status::Success`, in any other situation it must return `Status::Failure`, this also will cause to print error message and stop application.
|
||||
|
||||
Lastly if extension will be be dropped for some reason it will be detached from store and `teardown` function will be called.
|
||||
|
||||
1. `name`
|
||||
2. `init`
|
||||
3. `teardown`
|
||||
|
||||
== Example
|
||||
|
||||
[rust]
|
||||
```
|
||||
/// Mandatory for WASM runtime
|
||||
#[no_mangle]
|
||||
pub fn wapc_init() {
|
||||
wapc::register_function("name", name);
|
||||
wapc::register_function("init", init);
|
||||
wapc::register_function("teardown", teardown);
|
||||
wapc::register_function("create_payment", create_payment);
|
||||
wapc::register_function("cancel_order", cancel_order);
|
||||
}
|
||||
```
|
@ -9,6 +9,10 @@ pub const CONFIG_POS: u32 = 3;
|
||||
pub enum Error {
|
||||
#[error("Malformed create payment message")]
|
||||
MalformedCreatePayment,
|
||||
#[error("Provider rejected order")]
|
||||
PaymentFailed,
|
||||
#[error("HTTP request failed")]
|
||||
HttpFailed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
@ -54,7 +58,6 @@ pub struct Item {
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct CreatePayment {
|
||||
// pub client: PayUClient,
|
||||
pub buyer: Buyer,
|
||||
pub customer_ip: String,
|
||||
pub currency: String,
|
||||
@ -66,6 +69,30 @@ pub struct CreatePayment {
|
||||
pub continue_uri: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct OrderCreated {
|
||||
pub ext_order_id: ExtOrderId,
|
||||
pub redirect_uri: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[repr(C)]
|
||||
pub enum HttpMethod {
|
||||
Get,
|
||||
Post,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct HttpRequest {
|
||||
pub method: HttpMethod,
|
||||
pub url: String,
|
||||
pub headers: Vec<(String, String)>,
|
||||
pub bearer_auth: Option<String>,
|
||||
pub body: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
// #[tarpc::service]
|
||||
// pub trait PaymentAdapter {
|
||||
// async fn create_payment(msg: CreatePayment) -> Status;
|
||||
|
380
crates/payment_adapter_pay_u/Cargo.lock
generated
380
crates/payment_adapter_pay_u/Cargo.lock
generated
@ -38,12 +38,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.5.3"
|
||||
@ -95,12 +89,6 @@ version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
@ -290,21 +278,6 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
@ -424,31 +397,6 @@ dependencies = [
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2_wasi"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "756e9dfbaf90887b69ea8113ef9d04a9fec0ec4ac17b84a95006381f6f7966f9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio-util_wasi",
|
||||
"tokio_wasi",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@ -458,64 +406,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "hyper_wasi"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b903a973bd98c326103ea94336a9ffe44c57c71517d6be7639438021053dc6e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2_wasi",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"tokio_wasi",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
"wasmedge_wasi_socket",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.53"
|
||||
@ -561,16 +451,6 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@ -580,12 +460,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
@ -662,25 +536,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mio_wasi"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e260e68b7a4187e52caf00a035aaf7166391c2c2529123fe8229d4d8f86e15a2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasmedge_wasi_socket",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "model"
|
||||
version = "0.1.0"
|
||||
@ -776,7 +631,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -796,19 +651,6 @@ version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
|
||||
[[package]]
|
||||
name = "pay_u"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_more",
|
||||
"log",
|
||||
"reqwest_wasi",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "payment_adapter"
|
||||
version = "0.1.0"
|
||||
@ -827,9 +669,10 @@ name = "payment_adapter_pay_u"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"pay_u",
|
||||
"chrono",
|
||||
"payment_adapter",
|
||||
"tokio",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"wapc-codec",
|
||||
"wapc-guest",
|
||||
@ -929,41 +772,6 @@ version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest_wasi"
|
||||
version = "0.11.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c1c0524070b75dc4a924bdaac56d5c874205497fb3132f09f3270e4efb188d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2_wasi",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper_wasi",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio_wasi",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp"
|
||||
version = "0.8.11"
|
||||
@ -1050,18 +858,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
@ -1077,16 +873,6 @@ version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
@ -1159,49 +945,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"pin-project-lite",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util_wasi"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4e7e921c33dc681c0b01aa4c5aa1bf619f46a7040a1565ddb1f84bc4666c1bd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio_wasi",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio_wasi"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68e9a04f238fa1676caa7c897bf1dbe7a82269b675aca14176a028af7a2167d2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio_wasi",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"wasmedge_wasi_socket",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
@ -1211,12 +954,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
@ -1249,12 +986,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
@ -1330,16 +1061,6 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wapc-codec"
|
||||
version = "1.0.0"
|
||||
@ -1397,18 +1118,6 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
@ -1438,25 +1147,6 @@ version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wasmedge_wasi_socket"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fc4e93009d7e2c2e3d29b65d1f56ef606466673e85fe417808beca50cb608bf"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -1488,19 +1178,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.36.1",
|
||||
"windows_i686_gnu 0.36.1",
|
||||
"windows_i686_msvc 0.36.1",
|
||||
"windows_x86_64_gnu 0.36.1",
|
||||
"windows_x86_64_msvc 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
@ -1508,12 +1185,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1522,48 +1199,24 @@ version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
@ -1576,23 +1229,8 @@ version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
@ -7,10 +7,11 @@ edition = "2021"
|
||||
crate-type = ['cdylib']
|
||||
|
||||
[dependencies]
|
||||
pay_u = { path = "../../vendor/pay_u" }
|
||||
payment_adapter = { path = "../payment_adapter" }
|
||||
bincode = { version = "1.3.3" }
|
||||
wapc-codec = { version = "1.0.0" }
|
||||
wapc-guest = { version = "1.0.0" }
|
||||
tokio = { version="1.21.1", features=["rt","sync","time"] }
|
||||
tracing = { version = "0.1.37" }
|
||||
chrono = { version = "0.4.23", features = ['alloc', 'wasmbind'] }
|
||||
serde = { version = "1.0.149", features = ['derive'] }
|
||||
thiserror = { version = "1.0.37" }
|
||||
|
77
crates/payment_adapter_pay_u/src/credit.rs
Normal file
77
crates/payment_adapter_pay_u/src/credit.rs
Normal file
@ -0,0 +1,77 @@
|
||||
//! This module allow to create credit request during create order request
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{Address, ShoppingCart};
|
||||
|
||||
/// Describe customer during credit request
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Applicant {
|
||||
/// Applicant’s email address
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
email: Option<String>,
|
||||
/// Applicant’s phone number
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
phone: Option<String>,
|
||||
/// Applicant’s first name
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
first_name: Option<String>,
|
||||
/// Applicant’s last name
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
last_name: Option<String>,
|
||||
/// Language code, ISO-639-1 compliant. Denotes the language version of
|
||||
/// PayU hosted payment page and of e-mail messages sent from PayU to the
|
||||
/// payer (supported values are here).
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
language: Option<String>,
|
||||
/// National Identification Number
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
nin: Option<String>,
|
||||
/// Section containing data about applicant’s address.
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
address: Option<Address>,
|
||||
/// Additional information about person applying for credit.
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
additional_info: Option<ApplicantAdditionalInfo>,
|
||||
}
|
||||
|
||||
/// Allow to create credit request
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Credit {
|
||||
/// Section containing data of the ordered products
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
shopping_carts: Option<Vec<ShoppingCart>>,
|
||||
/// Section containing data of person applying for a credit
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
applicant: Option<Applicant>,
|
||||
}
|
||||
|
||||
impl Credit {
|
||||
pub fn with_shopping_carts<ShoppingCarts>(mut self, shopping_carts: ShoppingCarts) -> Self
|
||||
where
|
||||
ShoppingCarts: Iterator<Item = ShoppingCart>,
|
||||
{
|
||||
self.shopping_carts = Some(shopping_carts.collect());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplicantAdditionalInfo {
|
||||
/// Information whether there were previous, successfully completed orders
|
||||
/// for applicant.
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
has_successfully_finished_order_in_shop: Option<String>,
|
||||
}
|
162
crates/payment_adapter_pay_u/src/deserialize.rs
Normal file
162
crates/payment_adapter_pay_u/src/deserialize.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde::de::{self, Error, Visitor};
|
||||
|
||||
pub(crate) fn deserialize_i32<'de, D>(d: D) -> std::result::Result<i32, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
d.deserialize_string(I32Visitor)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_i32_newtype<'de, N: From<i32>, D>(
|
||||
d: D,
|
||||
) -> std::result::Result<N, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
d.deserialize_string(I32Visitor).map(N::from)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_u32<'de, D>(d: D) -> std::result::Result<u32, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
d.deserialize_string(U32Visitor)
|
||||
}
|
||||
|
||||
struct I32Visitor;
|
||||
|
||||
impl<'de> Visitor<'de> for I32Visitor {
|
||||
type Value = i32;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an integer between -2^31 and 2^31")
|
||||
}
|
||||
|
||||
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(i32::from(value))
|
||||
}
|
||||
|
||||
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
use std::i32;
|
||||
if value >= i64::from(i32::MIN) && value <= i64::from(i32::MAX) {
|
||||
Ok(value as i32)
|
||||
} else {
|
||||
Err(E::custom(format!("i32 out of range: {}", value)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
if let Ok(value) = v.parse::<i32>() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(E::custom(format!("str does not contains valid i32: {}", v)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
if let Ok(value) = v.parse::<i32>() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(E::custom(format!(
|
||||
"string does not contains valid i32: {}",
|
||||
v
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// Similar for other methods:
|
||||
// - visit_i16
|
||||
// - visit_u8
|
||||
// - visit_u16
|
||||
// - visit_u32
|
||||
// - visit_u64
|
||||
}
|
||||
|
||||
struct U32Visitor;
|
||||
|
||||
impl<'de> Visitor<'de> for U32Visitor {
|
||||
type Value = u32;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an integer between -2^31 and 2^31")
|
||||
}
|
||||
|
||||
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(u32::from(value))
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
use std::u32;
|
||||
if value >= u64::from(u32::MIN) && value <= u64::from(u32::MAX) {
|
||||
Ok(value as u32)
|
||||
} else {
|
||||
Err(E::custom(format!("i32 out of range: {}", value)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
if let Ok(value) = v.parse::<u32>() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(E::custom(format!("str does not contains valid i32: {}", v)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
if let Ok(value) = v.parse::<u32>() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(E::custom(format!(
|
||||
"string does not contains valid i32: {}",
|
||||
v
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// Similar for other methods:
|
||||
// - visit_i16
|
||||
// - visit_u8
|
||||
// - visit_u16
|
||||
// - visit_u32
|
||||
// - visit_u64
|
||||
}
|
@ -1,133 +1,247 @@
|
||||
mod credit;
|
||||
mod deserialize;
|
||||
mod model;
|
||||
mod req;
|
||||
mod res;
|
||||
mod serialize;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use pay_u::{ClientId, ClientSecret, MerchantPosId};
|
||||
use payment_adapter::*;
|
||||
use wapc_codec::messagepack::*;
|
||||
use wapc_guest as wapc;
|
||||
|
||||
use crate::req::OrderCreate;
|
||||
|
||||
struct PayU {
|
||||
client: Arc<Mutex<pay_u::Client>>,
|
||||
sandbox: bool,
|
||||
merchant_pos_id: i32,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
bearer: Option<String>,
|
||||
bearer_expires_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
static mut CLIENT: Option<PayU> = None;
|
||||
static mut RUNTIME: Option<tokio::runtime::Runtime> = None;
|
||||
impl PayU {
|
||||
/// Create new PayU client
|
||||
pub fn new(client_id: String, client_secret: String, merchant_pos_id: i32) -> Self {
|
||||
Self {
|
||||
bearer: None,
|
||||
sandbox: false,
|
||||
merchant_pos_id,
|
||||
client_id,
|
||||
client_secret,
|
||||
bearer_expires_at: chrono::Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn base_url(&self) -> &str {
|
||||
if self.sandbox {
|
||||
"https://secure.snd.payu.com/api/v2_1"
|
||||
} else {
|
||||
"https://secure.payu.com/api/v2_1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut CONFIG: Option<Arc<Mutex<PayU>>> = None;
|
||||
|
||||
fn client() -> Arc<Mutex<PayU>> {
|
||||
unsafe { CONFIG.as_ref().unwrap().clone() }
|
||||
}
|
||||
|
||||
impl PayU {
|
||||
fn mount() {
|
||||
wapc::register_function("teardown", Self::teardown);
|
||||
wapc::register_function("name", Self::name);
|
||||
wapc::register_function("init", Self::init);
|
||||
wapc::register_function("create_payment", Self::create_payment);
|
||||
wapc::register_function("cancel_order", Self::cancel_order);
|
||||
}
|
||||
|
||||
fn teardown(_msg: &[u8]) -> wapc::CallResult {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn name(_msg: &[u8]) -> wapc::CallResult {
|
||||
Ok(b"pay_u".to_vec())
|
||||
}
|
||||
|
||||
fn init(msg: &[u8]) -> wapc::CallResult {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap();
|
||||
unsafe {
|
||||
RUNTIME = Some(runtime);
|
||||
}
|
||||
|
||||
let config: PaymentProviderConfig = match deserialize(msg) {
|
||||
Ok(c) => c,
|
||||
_ => return Err(Box::new(payment_adapter::Error::MalformedCreatePayment)),
|
||||
_ => return Err(Box::new(Error::MalformedCreatePayment)),
|
||||
};
|
||||
let res_client = Arc::new(Mutex::new({
|
||||
pay_u::Client::new(
|
||||
ClientId::new(config.client_id.unwrap()),
|
||||
ClientSecret::new(config.client_secret.unwrap()),
|
||||
MerchantPosId::new(config.merchant_id.unwrap().parse().unwrap()),
|
||||
)
|
||||
}));
|
||||
|
||||
let c = res_client.clone();
|
||||
unsafe { RUNTIME.as_ref().unwrap() }.block_on(async {
|
||||
c.lock().unwrap().authorize().await.unwrap_or_else(|e| {
|
||||
// tracing::error!("{e}");
|
||||
dbg!(e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
let client = PayU::new(
|
||||
config.client_id.unwrap(),
|
||||
config.client_secret.unwrap(),
|
||||
config.merchant_id.unwrap().parse().unwrap(),
|
||||
);
|
||||
unsafe {
|
||||
CLIENT = Some(Self { client: res_client });
|
||||
CONFIG = Some(Arc::new(Mutex::new(client)));
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
Ok(serialize(Status::Success).unwrap_or_default())
|
||||
}
|
||||
|
||||
fn create_payment(msg: &[u8]) -> wapc::CallResult {
|
||||
let c: CreatePayment = match deserialize(msg) {
|
||||
Ok(c) => c,
|
||||
_ => return Err(Box::new(payment_adapter::Error::MalformedCreatePayment)),
|
||||
_ => return Err(Box::new(Error::MalformedCreatePayment)),
|
||||
};
|
||||
|
||||
let CreatePayment {
|
||||
client,
|
||||
buyer,
|
||||
customer_ip,
|
||||
currency,
|
||||
description,
|
||||
cart_products,
|
||||
mut items,
|
||||
order_ext_id,
|
||||
notify_uri,
|
||||
continue_uri,
|
||||
} = c;
|
||||
|
||||
let pay_u::res::CreateOrder {
|
||||
status: _,
|
||||
redirect_uri,
|
||||
order_id,
|
||||
ext_order_id: _,
|
||||
} = unsafe { RUNTIME.as_ref().unwrap() }.block_on(async {
|
||||
let client = unsafe { CLIENT.as_ref().unwrap() };
|
||||
client
|
||||
.create_order(
|
||||
pay_u::req::OrderCreate::build(
|
||||
buyer.into(),
|
||||
customer_ip,
|
||||
currency,
|
||||
description,
|
||||
)
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::InvalidOrder
|
||||
})?
|
||||
.with_products(cart_products.into_iter().map(|p| {
|
||||
pay_u::Product::new(
|
||||
p.name.to_string(),
|
||||
**p.price,
|
||||
items
|
||||
.remove(&p.id)
|
||||
.map(|(quantity, _)| **quantity as u32)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}))
|
||||
.with_ext_order_id(order_ext_id)
|
||||
.with_notify_url(notify_uri)
|
||||
.with_continue_url(continue_uri),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
let _ = Ok((redirect_uri, ExtOrderId::new(order_id.0)));
|
||||
|
||||
wapc::console_log(&format!(
|
||||
"IN_WASM: Received request for `ping` operation with payload : {:?}",
|
||||
c
|
||||
));
|
||||
eprintln!("{:?}", c);
|
||||
// let _res = wapc::host_call("binding", "sample:namespace", "pong", msg)?;
|
||||
Ok(serialize(Status::Success).unwrap())
|
||||
|
||||
let res::CreateOrder {
|
||||
status: _,
|
||||
redirect_uri,
|
||||
order_id,
|
||||
ext_order_id: _,
|
||||
} = create_payment(c)?;
|
||||
Ok(serialize(OrderCreated {
|
||||
redirect_uri,
|
||||
ext_order_id: ExtOrderId::new(order_id),
|
||||
})
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
fn cancel_order(_msg: &[u8]) -> wapc::CallResult {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn create_payment(c: CreatePayment) -> Result<res::CreateOrder, Error> {
|
||||
let config = client();
|
||||
let pay_u = config.lock().unwrap();
|
||||
|
||||
let CreatePayment {
|
||||
buyer,
|
||||
customer_ip,
|
||||
currency,
|
||||
description,
|
||||
cart_products,
|
||||
items,
|
||||
order_ext_id,
|
||||
notify_uri,
|
||||
continue_uri,
|
||||
} = c;
|
||||
|
||||
let quantities = {
|
||||
let len = items.len();
|
||||
items
|
||||
.iter()
|
||||
.fold(HashMap::with_capacity(len), |mut h, item| {
|
||||
h.insert(item.product_id, item.quantity);
|
||||
h
|
||||
})
|
||||
};
|
||||
let create_order = OrderCreate::build(
|
||||
{
|
||||
let Buyer {
|
||||
email,
|
||||
phone,
|
||||
first_name,
|
||||
last_name,
|
||||
language,
|
||||
} = buyer;
|
||||
|
||||
model::Buyer::new(email, phone, first_name, last_name, language)
|
||||
},
|
||||
customer_ip,
|
||||
currency,
|
||||
description,
|
||||
)
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::MalformedCreatePayment
|
||||
})?
|
||||
.with_products(cart_products.into_iter().map(|p| {
|
||||
model::Product::new(
|
||||
p.name.to_string(),
|
||||
p.unit_price,
|
||||
quantities.get(&p.id).copied().unwrap_or_default() as model::Quantity,
|
||||
)
|
||||
}))
|
||||
.with_ext_order_id(order_ext_id)
|
||||
.with_notify_url(notify_uri)
|
||||
.with_continue_url(continue_uri);
|
||||
|
||||
authorize()?;
|
||||
let res = wapc::host_call(
|
||||
"binding",
|
||||
"http:req",
|
||||
"http_req",
|
||||
&serialize(HttpRequest {
|
||||
method: HttpMethod::Post,
|
||||
url: format!("{}/orders", pay_u.base_url()),
|
||||
headers: vec![],
|
||||
bearer_auth: pay_u.bearer.clone(),
|
||||
body: serialize(create_order).ok(),
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})
|
||||
.and_then(|v| {
|
||||
deserialize(&v).map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})
|
||||
})?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn wapc_init() {
|
||||
PayU::mount();
|
||||
}
|
||||
|
||||
fn authorize() -> Result<bool, Error> {
|
||||
use chrono::{Duration, Utc};
|
||||
|
||||
let config = client();
|
||||
let mut pay_u = config.lock().unwrap();
|
||||
|
||||
if Utc::now() - Duration::seconds(1) < pay_u.bearer_expires_at {
|
||||
return Ok(true);
|
||||
}
|
||||
#[derive(serde::Deserialize)]
|
||||
struct BearerResult {
|
||||
access_token: String,
|
||||
expires_in: i64,
|
||||
}
|
||||
|
||||
let res = wapc::host_call("binding", "http:req", "http_req", &serialize(HttpRequest {
|
||||
method: HttpMethod::Post,
|
||||
url: format!(
|
||||
"https://secure.payu.com/pl/standard/user/oauth/authorize?grant_type=client_credentials&client_id={}&client_secret={}",
|
||||
pay_u.client_id,
|
||||
pay_u.client_secret
|
||||
),
|
||||
headers: vec![],
|
||||
bearer_auth: None,
|
||||
body: None,
|
||||
}).map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})?).map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})?;
|
||||
let res: BearerResult = deserialize(&res).map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})?;
|
||||
tracing::trace!("Bearer is {}", res.access_token);
|
||||
pay_u.bearer_expires_at = Utc::now() + Duration::seconds(res.expires_in);
|
||||
pay_u.bearer = Some(res.access_token);
|
||||
Ok(true)
|
||||
}
|
||||
|
700
crates/payment_adapter_pay_u/src/model.rs
Normal file
700
crates/payment_adapter_pay_u/src/model.rs
Normal file
@ -0,0 +1,700 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{deserialize, serialize};
|
||||
|
||||
pub static SUCCESS: &str = "SUCCESS";
|
||||
|
||||
pub type OrderId = String;
|
||||
pub type MerchantPosId = i32;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Client is not authorized. No bearer token available")]
|
||||
NoToken,
|
||||
#[error("Invalid customer ip. IP 0.0.0.0 is not acceptable")]
|
||||
CustomerIp,
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("Total value is not sum of products price")]
|
||||
IncorrectTotal,
|
||||
#[error("Buyer is required to place an order")]
|
||||
NoBuyer,
|
||||
#[error("Description is required to place an order")]
|
||||
NoDescription,
|
||||
#[error("Client is not authorized")]
|
||||
Unauthorized,
|
||||
#[error("Refund returned invalid response")]
|
||||
Refund,
|
||||
#[error("Create order returned invalid response")]
|
||||
CreateOrder,
|
||||
#[error("Failed to fetch order transactions")]
|
||||
OrderTransactions,
|
||||
#[error("Failed to fetch order details")]
|
||||
OrderDetails,
|
||||
#[error("Failed to fetch order refunds")]
|
||||
OrderRefunds,
|
||||
#[error("PayU rejected to create order with status {status_code:?}")]
|
||||
CreateFailed {
|
||||
status_code: String,
|
||||
status_desc: Option<String>,
|
||||
code: Option<String>,
|
||||
severity: Option<String>,
|
||||
code_literal: Option<CodeLiteral>,
|
||||
},
|
||||
#[error("PayU rejected to perform refund with status {status_code:?}")]
|
||||
RefundFailed {
|
||||
status_code: String,
|
||||
status_desc: Option<String>,
|
||||
code: Option<String>,
|
||||
severity: Option<String>,
|
||||
code_literal: Option<CodeLiteral>,
|
||||
},
|
||||
#[error("PayU rejected order details request with status {status_code:?}")]
|
||||
OrderDetailsFailed {
|
||||
status_code: String,
|
||||
status_desc: Option<String>,
|
||||
code: Option<String>,
|
||||
severity: Option<String>,
|
||||
code_literal: Option<CodeLiteral>,
|
||||
},
|
||||
#[error("PayU rejected order transactions details request with status {status_code:?}")]
|
||||
OrderTransactionsFailed {
|
||||
status_code: String,
|
||||
status_desc: Option<String>,
|
||||
code: Option<String>,
|
||||
severity: Option<String>,
|
||||
code_literal: Option<CodeLiteral>,
|
||||
},
|
||||
#[error("PayU returned order details but without any order")]
|
||||
NoOrderInDetails,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum CodeLiteral {
|
||||
/// Request lacks "refund" object.
|
||||
MissingRefundSection,
|
||||
|
||||
/// Transaction has not been finalized
|
||||
TransNotEnded,
|
||||
|
||||
/// Lack of funds in account
|
||||
NoBalance,
|
||||
|
||||
/// Refund amount exceeds transaction amount
|
||||
AmountToBig,
|
||||
|
||||
/// Refund value is too small
|
||||
AmountToSmall,
|
||||
|
||||
/// Refunds have been disabled
|
||||
RefundDisabled,
|
||||
|
||||
/// Too many refund attempts have been made
|
||||
RefundToOften,
|
||||
|
||||
/// Refund was already created
|
||||
Paid,
|
||||
|
||||
/// Unknown error
|
||||
UnknownError,
|
||||
|
||||
/// extRefundId was re-used and other params do not match the values
|
||||
/// sent during the first call.
|
||||
RefundIdempotencyMismatch,
|
||||
|
||||
/// Shop billing has not yet been completed
|
||||
TransBillingEntriesNotCompleted,
|
||||
|
||||
/// The available time for refund has passed.
|
||||
TransTooOld,
|
||||
|
||||
/// Transaction amount that remains after refund creation will be too
|
||||
/// small to make another refund.
|
||||
RemainingTransAmountTooSmall,
|
||||
|
||||
#[serde(other)]
|
||||
/// Implementation changed
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Prop {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Refund {
|
||||
pub refund_id: String,
|
||||
pub ext_refund_id: Option<String>,
|
||||
pub amount: String,
|
||||
pub currency_code: String,
|
||||
pub description: String,
|
||||
pub creation_date_time: String,
|
||||
pub status: String,
|
||||
pub status_date_time: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Status {
|
||||
/// One of
|
||||
/// * `PENDING`: Payment is currently being processed.
|
||||
/// * `WAITING_FOR_CONFIRMATION`: PayU is currently waiting for the merchant
|
||||
/// system to receive (capture) the payment. This status is set if
|
||||
/// auto-receive is disabled on the merchant system.
|
||||
/// * `COMPLETED`: Payment has been accepted. PayU will pay out the funds
|
||||
/// shortly.
|
||||
/// * `CANCELED`: Payment has been cancelled and the buyer has not been
|
||||
/// charged (no money was taken from buyer's account).
|
||||
///
|
||||
/// > Too prevent sending wrong status from server to PayU this field
|
||||
/// > remains String
|
||||
status_code: String,
|
||||
status_desc: Option<String>,
|
||||
code: Option<String>,
|
||||
severity: Option<String>,
|
||||
code_literal: Option<CodeLiteral>,
|
||||
}
|
||||
|
||||
impl Status {
|
||||
/// Check if http request was successful
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pay_u::Status;
|
||||
/// let status: Status = serde_json::from_str("{\"statusCode\":\"SUCCESS\"}").unwrap();
|
||||
/// assert_eq!(status.is_success(), true);
|
||||
/// ```
|
||||
pub fn is_success(&self) -> bool {
|
||||
self.status_code.as_str() == SUCCESS
|
||||
}
|
||||
|
||||
/// Returns http status
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pay_u::Status;
|
||||
/// let status: Status = serde_json::from_str("{\"statusCode\":\"SUCCESS\"}").unwrap();
|
||||
/// assert_eq!(status.status_code(), "SUCCESS");
|
||||
/// ```
|
||||
pub fn status_code(&self) -> &str {
|
||||
&self.status_code
|
||||
}
|
||||
|
||||
pub fn status_desc(&self) -> Option<&str> {
|
||||
self.status_desc.as_deref()
|
||||
}
|
||||
|
||||
pub fn code(&self) -> Option<&str> {
|
||||
self.code.as_deref()
|
||||
}
|
||||
|
||||
pub fn severity(&self) -> Option<&str> {
|
||||
self.severity.as_deref()
|
||||
}
|
||||
|
||||
pub fn code_literal(&self) -> Option<&CodeLiteral> {
|
||||
self.code_literal.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// PayU payment status.
|
||||
///
|
||||
/// Each payment is initially Pending and can change according to following
|
||||
/// graph:
|
||||
///
|
||||
/// <img src="https://developers.payu.com/images/order_statusesV2-en.png">
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum PaymentStatus {
|
||||
/// Payment is currently being processed.
|
||||
Pending,
|
||||
/// PayU is currently waiting for the merchant system to receive (capture)
|
||||
/// the payment. This status is set if auto-receive is disabled on the
|
||||
/// merchant system.
|
||||
WaitingForConfirmation,
|
||||
/// Payment has been accepted. PayU will pay out the funds shortly.
|
||||
Completed,
|
||||
/// Payment has been cancelled and the buyer has not been charged (no money
|
||||
/// was taken from buyer's account).
|
||||
Canceled,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum RefundStatus {
|
||||
/// refund was completed successfully
|
||||
Finalized,
|
||||
/// refund was cancelled
|
||||
Canceled,
|
||||
/// refund in progress
|
||||
Pending,
|
||||
/// PayU is currently waiting for the merchant system to receive (capture)
|
||||
/// the payment. This status is set if auto-receive is disabled on the
|
||||
/// merchant system.
|
||||
WaitingForConfirmation,
|
||||
/// Payment has been accepted. PayU will pay out the funds shortly.
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Delivery {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Street name
|
||||
street: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Postal box number
|
||||
postal_box: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Postal code
|
||||
postal_code: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// City
|
||||
city: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Province
|
||||
state: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Two-letter country code compliant with ISO-3166.
|
||||
country_code: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Address description
|
||||
name: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Recipient’s name
|
||||
recipient_name: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Recipient’s e-mail address
|
||||
recipient_email: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Recipient’s phone number
|
||||
recipient_phone: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BuyerShippingAddress {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// stores the shipping address
|
||||
delivery: Option<Delivery>,
|
||||
}
|
||||
|
||||
impl BuyerShippingAddress {
|
||||
pub fn new_with_delivery(delivery: Delivery) -> Self {
|
||||
Self {
|
||||
delivery: Some(delivery),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Buyer {
|
||||
/// Required customer e-mail
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
email: Option<String>,
|
||||
/// Required customer phone number
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
phone: Option<String>,
|
||||
/// Required customer first name
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
first_name: Option<String>,
|
||||
/// Required customer last name
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
last_name: Option<String>,
|
||||
/// Required customer language
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
language: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
delivery: Option<BuyerShippingAddress>,
|
||||
}
|
||||
|
||||
impl Buyer {
|
||||
pub fn new<Email, Phone, FirstName, LastName, Language>(
|
||||
email: Email,
|
||||
phone: Phone,
|
||||
first_name: FirstName,
|
||||
last_name: LastName,
|
||||
lang: Language,
|
||||
) -> Self
|
||||
where
|
||||
Email: Into<String>,
|
||||
Phone: Into<String>,
|
||||
FirstName: Into<String>,
|
||||
LastName: Into<String>,
|
||||
Language: Into<String>,
|
||||
{
|
||||
Self {
|
||||
email: Some(email.into()),
|
||||
phone: Some(phone.into()),
|
||||
first_name: Some(first_name.into()),
|
||||
last_name: Some(last_name.into()),
|
||||
language: Some(lang.into()),
|
||||
delivery: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn email(&self) -> &str {
|
||||
self.email.as_deref().unwrap_or_default()
|
||||
}
|
||||
pub fn with_email<S>(mut self, email: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.email = Some(email.into());
|
||||
self
|
||||
}
|
||||
pub fn phone(&self) -> &str {
|
||||
self.phone.as_deref().unwrap_or_default()
|
||||
}
|
||||
pub fn with_phone<S>(mut self, phone: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.phone = Some(phone.into());
|
||||
self
|
||||
}
|
||||
pub fn first_name(&self) -> &str {
|
||||
self.first_name.as_deref().unwrap_or_default()
|
||||
}
|
||||
pub fn with_first_name<S>(mut self, first_name: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.first_name = Some(first_name.into());
|
||||
self
|
||||
}
|
||||
pub fn last_name(&self) -> &str {
|
||||
self.last_name.as_deref().unwrap_or_default()
|
||||
}
|
||||
pub fn with_last_name<S>(mut self, last_name: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.last_name = Some(last_name.into());
|
||||
self
|
||||
}
|
||||
pub fn language(&self) -> &str {
|
||||
self.language.as_deref().unwrap_or_default()
|
||||
}
|
||||
pub fn with_language<S>(mut self, language: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.language = Some(language.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_delivery(mut self, delivery: Delivery) -> Self {
|
||||
self.delivery = Some(BuyerShippingAddress::new_with_delivery(delivery));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub type Price = i32;
|
||||
pub type Quantity = u32;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Product {
|
||||
pub name: String,
|
||||
#[serde(
|
||||
serialize_with = "serialize::serialize_i32",
|
||||
deserialize_with = "deserialize::deserialize_i32"
|
||||
)]
|
||||
pub unit_price: Price,
|
||||
#[serde(
|
||||
serialize_with = "serialize::serialize_u32",
|
||||
deserialize_with = "deserialize::deserialize_u32"
|
||||
)]
|
||||
pub quantity: Quantity,
|
||||
/// Product type, which can be virtual or material; (possible values true or
|
||||
/// false).
|
||||
#[serde(rename = "virtual", skip_serializing_if = "Option::is_none")]
|
||||
pub virtual_product: Option<bool>,
|
||||
/// Marketplace date from which the product (or offer) is available, ISO
|
||||
/// format applies, e.g. "2019-03-27T10:57:59.000+01:00".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub listing_date: Option<chrono::NaiveDateTime>,
|
||||
}
|
||||
|
||||
impl Product {
|
||||
pub fn new<Name: Into<String>>(name: Name, unit_price: Price, quantity: Quantity) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
unit_price,
|
||||
quantity,
|
||||
virtual_product: None,
|
||||
listing_date: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Product type, which can be virtual or material; (possible values true or
|
||||
/// false).
|
||||
pub fn into_virtual(mut self) -> Self {
|
||||
self.virtual_product = Some(true);
|
||||
self
|
||||
}
|
||||
|
||||
/// Product type, which can be virtual or material; (possible values true or
|
||||
/// false).
|
||||
pub fn non_virtual(mut self) -> Self {
|
||||
self.virtual_product = Some(false);
|
||||
self
|
||||
}
|
||||
|
||||
/// Marketplace date from which the product (or offer) is available, ISO
|
||||
/// format applies, e.g. "2019-03-27T10:57:59.000+01:00".
|
||||
pub fn with_listing_date(mut self, listing_date: chrono::NaiveDateTime) -> Self {
|
||||
self.listing_date = Some(listing_date);
|
||||
self
|
||||
}
|
||||
|
||||
fn erase_listing_date(mut self) -> Self {
|
||||
self.listing_date = None;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ShoppingCart {
|
||||
/// Section containing data of shipping method.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
shopping_method: Option<ShoppingMethod>,
|
||||
/// Section containing data about ordered products.
|
||||
/// > Note: product objects in the <shoppingCart.products> section do not
|
||||
/// > have a listingDate field
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
products: Option<Vec<Product>>,
|
||||
/// Submerchant identifier. This field should be consistent with field
|
||||
/// extCustomerId in shoppingCarts section when order is placed in
|
||||
/// marketplace.
|
||||
ext_customer_id: String,
|
||||
}
|
||||
|
||||
impl ShoppingCart {
|
||||
pub fn new<ExtCustomerId, Products>(ext_customer_id: ExtCustomerId) -> Self
|
||||
where
|
||||
ExtCustomerId: Into<String>,
|
||||
{
|
||||
Self {
|
||||
shopping_method: None,
|
||||
ext_customer_id: ext_customer_id.into(),
|
||||
products: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_products<ExtCustomerId, Products>(
|
||||
ext_customer_id: ExtCustomerId,
|
||||
products: Products,
|
||||
) -> Self
|
||||
where
|
||||
ExtCustomerId: Into<String>,
|
||||
Products: Iterator<Item = Product>,
|
||||
{
|
||||
Self {
|
||||
shopping_method: None,
|
||||
ext_customer_id: ext_customer_id.into(),
|
||||
products: Some(products.map(Product::erase_listing_date).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_products<Products>(mut self, products: Products) -> Self
|
||||
where
|
||||
Products: Iterator<Item = Product>,
|
||||
{
|
||||
self.products = Some(products.map(Product::erase_listing_date).collect());
|
||||
self
|
||||
}
|
||||
|
||||
/// Section containing data of shipping method.
|
||||
pub fn shopping_method(&self) -> &Option<ShoppingMethod> {
|
||||
&self.shopping_method
|
||||
}
|
||||
|
||||
/// Section containing data about ordered products.
|
||||
/// > Note: product objects in the <shoppingCart.products> section do not
|
||||
/// > have a listingDate field
|
||||
pub fn products(&self) -> &Option<Vec<Product>> {
|
||||
&self.products
|
||||
}
|
||||
|
||||
/// Submerchant identifier. This field should be consistent with field
|
||||
/// extCustomerId in shoppingCarts section when order is placed in
|
||||
/// marketplace.
|
||||
pub fn ext_customer_id(&self) -> &String {
|
||||
&self.ext_customer_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of shipment
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum ShoppingMethodType {
|
||||
Courier,
|
||||
CollectionPointPickup,
|
||||
ParcelLocker,
|
||||
StorePickup,
|
||||
}
|
||||
|
||||
/// Delivery address
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Address {
|
||||
/// The full name of the pickup point, including its unique identifier, e.g.
|
||||
/// „Parcel locker POZ29A”.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub point_id: Option<String>,
|
||||
/// Street name, possibly including house and flat number.
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub street: Option<String>,
|
||||
/// Street number
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub street_no: Option<String>,
|
||||
/// Flat number
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub flat_no: Option<String>,
|
||||
/// Postal code
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub postal_code: Option<String>,
|
||||
/// City
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub city: Option<String>,
|
||||
/// Two-letter country code compliant with ISO-3166
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub country_code: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ShoppingMethod {
|
||||
/// Shipping type
|
||||
/// Recommended
|
||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||
pub shopping_type: Option<ShoppingMethodType>,
|
||||
/// Shipping cost
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub price: Option<String>,
|
||||
/// Section containing data about shipping address.
|
||||
/// Recommended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub address: Option<Address>,
|
||||
}
|
||||
|
||||
/// MultiUseCartToken
|
||||
pub mod muct {
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum CardOnFile {
|
||||
/// Payment initialized by the card owner who agreed to save card for
|
||||
/// future use. You can expect full authentication (3D Secure
|
||||
/// and/or CVV). If you want to use multi-use token (TOKC_)
|
||||
/// later, you have to be confident, that first payment was
|
||||
/// successful. Default value for single-use token (TOK_).
|
||||
///
|
||||
/// In case of plain card data payments you should retrieve transaction
|
||||
/// data to obtain first TransactionId. It should be passed in
|
||||
/// payMethods.payMethod.card section for transactions marked as
|
||||
/// STANDARD, STANDARD_CARDHOLDER and STANDARD_MERCHANT;
|
||||
/// STANDARD_CARDHOLDER - payment with already saved card,
|
||||
/// initialized by the card owner. This transaction has
|
||||
/// multi-use token (TOKC_). Depending of payment parameters
|
||||
/// (e.g. high transaction amount) strong authentication can be
|
||||
/// expected (3D Secure and/or CVV). Default value for multi-use token
|
||||
/// (TOKC_);
|
||||
First,
|
||||
/// Payment with already saved card, initialized by the shop without the
|
||||
/// card owner participation. This transaction has multi-use token
|
||||
/// (TOKC_). By the definition, this payment type does not
|
||||
/// require strong authentication. You cannot use it if FIRST
|
||||
/// card-on-file payment failed.
|
||||
StandardMerchant,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum Recurring {
|
||||
/// Payment initialized by the card owner who agreed to save card for
|
||||
/// future use in recurring plan. You can expect full authentication (3D
|
||||
/// Secure and/or CVV). If you want to use multi-use token (TOKC_)
|
||||
/// later, you have to be confident, that first recurring
|
||||
/// payment was successful.
|
||||
First,
|
||||
/// Subsequent recurring payment (user is not present). This transaction
|
||||
/// has multi use token (TOKC_). You cannot use it if FIRST recurring
|
||||
/// payment failed.
|
||||
Standard,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MultiUseCartToken {
|
||||
/// Information about party initializing order:
|
||||
///
|
||||
/// * `FIRST` - payment initialized by the card owner who agreed to save
|
||||
/// card for future use. You can expect full authentication (3D Secure
|
||||
/// and/or CVV). If you want to use multi-use token (TOKC_) later, you
|
||||
/// have to be confident, that first payment was successful. Default
|
||||
/// value for single-use token (TOK_).
|
||||
///
|
||||
/// In case of plain card data payments you should retrieve
|
||||
/// transaction data to obtain first TransactionId. It should
|
||||
/// be passed in payMethods.payMethod.card section for
|
||||
/// transactions marked as STANDARD, STANDARD_CARDHOLDER and
|
||||
/// STANDARD_MERCHANT; STANDARD_CARDHOLDER - payment with
|
||||
/// already saved card, initialized by the card owner. This
|
||||
/// transaction has multi-use token (TOKC_). Depending of payment
|
||||
/// parameters (e.g. high transaction amount) strong authentication
|
||||
/// can be expected (3D Secure and/or CVV). Default value for
|
||||
/// multi-use token (TOKC_);
|
||||
/// * `STANDARD_MERCHANT` - payment with already saved card, initialized
|
||||
/// by the shop without the card owner participation. This transaction
|
||||
/// has multi-use token (TOKC_). By the definition, this payment type
|
||||
/// does not require strong authentication. You cannot use it if FIRST
|
||||
/// card-on-file payment failed.
|
||||
///
|
||||
/// `cardOnFile` parameter cannot be used with recurring parameter.
|
||||
pub card_on_file: CardOnFile,
|
||||
/// Marks the order as recurring payment.
|
||||
///
|
||||
/// * `FIRST` - payment initialized by the card owner who agreed to save
|
||||
/// card for future use in recurring plan. You can expect full
|
||||
/// authentication (3D Secure and/or CVV). If you want to use
|
||||
/// multi-use token (TOKC_) later, you have to be confident, that
|
||||
/// first recurring payment was successful.
|
||||
/// * `STANDARD` - subsequent recurring payment (user is not present).
|
||||
/// This transaction has multi use token (TOKC_). You cannot use it if
|
||||
/// FIRST recurring payment failed.
|
||||
///
|
||||
/// `recurring` parameter cannot be used with cardOnFile parameter.
|
||||
pub recurring: Recurring,
|
||||
}
|
||||
}
|
382
crates/payment_adapter_pay_u/src/req.rs
Normal file
382
crates/payment_adapter_pay_u/src/req.rs
Normal file
@ -0,0 +1,382 @@
|
||||
//! Objects used to send requests to PayU
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::credit::Credit;
|
||||
pub use crate::model::*;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Refund {
|
||||
pub(crate) description: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) amount: Option<Price>,
|
||||
}
|
||||
|
||||
impl Refund {
|
||||
pub fn new<Description>(description: Description, amount: Option<Price>) -> Self
|
||||
where
|
||||
Description: Into<String>,
|
||||
{
|
||||
Self {
|
||||
description: description.into(),
|
||||
amount,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &str {
|
||||
&self.description
|
||||
}
|
||||
|
||||
pub fn amount(&self) -> Option<Price> {
|
||||
self.amount
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrderCreate {
|
||||
/// ID of an order used in merchant system. Order identifier assigned by
|
||||
/// the merchant. It enables merchants to find a specific order
|
||||
/// in their system. This value must be unique within a single
|
||||
/// POS.
|
||||
pub(crate) ext_order_id: Option<String>,
|
||||
/// URL to which web hook will be send. It's important to return 200 to
|
||||
/// all notifications.
|
||||
///
|
||||
/// All notifications are send as POST with JSON payload
|
||||
///
|
||||
/// Notifications are sent immediately after a payment status changes.
|
||||
/// If the notification is not received by the Shop application,
|
||||
/// it will be sent again in accordance with the table below:
|
||||
///
|
||||
/// | Attempt | Time |
|
||||
/// |---------|------|
|
||||
/// | 1 | immediately |
|
||||
/// | 2 | 1 minute |
|
||||
/// | 3 | 2 minutes |
|
||||
/// | 4 | 5 minutes |
|
||||
/// | 5 | 10 minutes |
|
||||
/// | 6 | 30 minutes |
|
||||
/// | 7 | 1 hour |
|
||||
/// | 8 | 2 hours |
|
||||
/// | 9 | 3 hours |
|
||||
/// | 10| 6 hours |
|
||||
/// | 11| 9 hours |
|
||||
/// | 12| 12 hours |
|
||||
/// | 13| 15 hours |
|
||||
/// | 14| 18 hours |
|
||||
/// | 15| 21 hours |
|
||||
/// | 16| 24 hours |
|
||||
/// | 17| 36 hours |
|
||||
/// | 18| 48 hours |
|
||||
/// | 19| 60 hours |
|
||||
/// | 20| 72 hours |
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) notify_url: Option<String>,
|
||||
/// Address for redirecting the customer after payment is commenced. If
|
||||
/// the payment has not been authorized, error=501 parameter
|
||||
/// will be added. Please note that no decision regarding
|
||||
/// payment status should be made depending on the presence or
|
||||
/// lack of this parameter (to get payment status, wait for
|
||||
/// notification or retrieve order details).
|
||||
///
|
||||
/// IMPORTANT: the address must be compliant with the structure below:
|
||||
/// <img src="https://developers.payu.com/images/continueUrlStructure_en.png" />
|
||||
///
|
||||
/// Please keep in mind:
|
||||
/// * accepted schemas are http and https,
|
||||
/// * such elements as port, path, query and fragment are optional,
|
||||
/// * query values must be encoded.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) continue_url: Option<String>,
|
||||
/// Payer’s IP address, e.g. 123.123.123.123. Note: 0.0.0.0 is not
|
||||
/// accepted.
|
||||
pub(crate) customer_ip: String,
|
||||
/// Secret pos ip. This is connected to PayU account
|
||||
#[serde(
|
||||
serialize_with = "crate::serialize::serialize_newtype",
|
||||
deserialize_with = "crate::deserialize::deserialize_i32_newtype"
|
||||
)]
|
||||
pub(crate) merchant_pos_id: MerchantPosId,
|
||||
/// Transaction description
|
||||
pub(crate) description: String,
|
||||
/// 3 characters currency identifier, ex. PLN
|
||||
pub(crate) currency_code: String,
|
||||
/// Total price of the order in pennies (e.g. 1000 is 10.00 EUR).
|
||||
/// Applies also to currencies without subunits (e.g. 1000 is 10
|
||||
/// HUF).
|
||||
#[serde(
|
||||
serialize_with = "crate::serialize::serialize_i32",
|
||||
deserialize_with = "crate::deserialize::deserialize_i32"
|
||||
)]
|
||||
pub(crate) total_amount: Price,
|
||||
/// @see [crate::Buyer]
|
||||
pub(crate) buyer: Option<Buyer>,
|
||||
/// List of products
|
||||
pub(crate) products: Vec<Product>,
|
||||
#[serde(skip_serializing)]
|
||||
pub(crate) order_create_date: Option<String>,
|
||||
/// Duration for the validity of an order (in seconds), during which
|
||||
/// time payment must be made. Default value 86400.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) validity_time: Option<u16>,
|
||||
/// Additional description of the order.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) additional_description: Option<String>,
|
||||
/// Text visible on the PayU payment page (max. 80 chars).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) visible_description: Option<String>,
|
||||
/// Payment recipient name followed by payment description (order ID,
|
||||
/// ticket number etc) visible on card statement (max. 22
|
||||
/// chars). The name should be easy to recognize by the
|
||||
/// cardholder (e.g "shop.com 124343"). If field
|
||||
/// is not provided, static name configured by PayU will be used.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) statement_description: Option<String>,
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) muct: Option<muct::MultiUseCartToken>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub(crate) credit: Option<Credit>,
|
||||
}
|
||||
|
||||
impl OrderCreate {
|
||||
pub fn build<CustomerIp, Currency, Description>(
|
||||
buyer: Buyer,
|
||||
customer_ip: CustomerIp,
|
||||
currency: Currency,
|
||||
description: Description,
|
||||
) -> Result<Self>
|
||||
where
|
||||
CustomerIp: Into<String>,
|
||||
Currency: Into<String>,
|
||||
Description: Into<String>,
|
||||
{
|
||||
let customer_ip = customer_ip.into();
|
||||
if &customer_ip == "0.0.0.0" {
|
||||
return Err(Error::CustomerIp);
|
||||
}
|
||||
Ok(Self {
|
||||
ext_order_id: None,
|
||||
notify_url: None,
|
||||
continue_url: None,
|
||||
customer_ip,
|
||||
merchant_pos_id: 0,
|
||||
description: description.into(),
|
||||
currency_code: currency.into(),
|
||||
total_amount: 0,
|
||||
buyer: Some(buyer),
|
||||
products: Vec::new(),
|
||||
order_create_date: None,
|
||||
validity_time: None,
|
||||
additional_description: None,
|
||||
visible_description: None,
|
||||
statement_description: None,
|
||||
muct: None,
|
||||
credit: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// ID of an order used in merchant system. Order identifier assigned by
|
||||
/// the merchant. It enables merchants to find a specific order
|
||||
/// in their system. This value must be unique within a single
|
||||
/// POS.
|
||||
pub fn with_ext_order_id<S: Into<String>>(mut self, ext_order_id: S) -> Self {
|
||||
self.ext_order_id = Some(ext_order_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Duration for the validity of an order (in seconds), during which
|
||||
/// time payment must be made. Default value 86400.
|
||||
pub fn with_validity_time(mut self, validity_time: u16) -> Self {
|
||||
self.validity_time = Some(validity_time);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_multi_use_token(
|
||||
mut self,
|
||||
recurring: muct::Recurring,
|
||||
card_on_file: muct::CardOnFile,
|
||||
) -> Self {
|
||||
self.muct = Some(muct::MultiUseCartToken {
|
||||
recurring,
|
||||
card_on_file,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_products<Products>(mut self, products: Products) -> Self
|
||||
where
|
||||
Products: Iterator<Item = Product>,
|
||||
{
|
||||
self.products.extend(products);
|
||||
self.total_amount = self
|
||||
.products
|
||||
.iter()
|
||||
.fold(0, |agg, p| agg + (p.quantity as i32 * p.unit_price as i32));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_product(mut self, product: Product) -> Self {
|
||||
self.products.push(product);
|
||||
self.total_amount = self
|
||||
.products
|
||||
.iter()
|
||||
.fold(0, |agg, p| agg + (p.quantity as i32 * p.unit_price as i32));
|
||||
self
|
||||
}
|
||||
|
||||
/// Description of the an order.
|
||||
///
|
||||
/// > This method will override initial description!
|
||||
pub fn with_description<Description>(mut self, desc: Description) -> Self
|
||||
where
|
||||
Description: Into<String>,
|
||||
{
|
||||
self.description = String::from(desc.into().trim());
|
||||
self
|
||||
}
|
||||
|
||||
/// Additional description of the order.
|
||||
pub fn with_additional_description<S: Into<String>>(
|
||||
mut self,
|
||||
additional_description: S,
|
||||
) -> Self {
|
||||
self.additional_description = Some(additional_description.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Text visible on the PayU payment page (max. 80 chars).
|
||||
pub fn with_visible_description(mut self, visible_description: &str) -> Self {
|
||||
let visible_description = if visible_description.len() > 60 {
|
||||
&visible_description[..60]
|
||||
} else {
|
||||
visible_description
|
||||
};
|
||||
self.visible_description = Some(String::from(visible_description));
|
||||
self
|
||||
}
|
||||
|
||||
/// Payment recipient name followed by payment description (order ID,
|
||||
/// ticket number etc) visible on card statement (max. 22
|
||||
/// chars). The name should be easy to recognize by the
|
||||
/// cardholder (e.g "shop.com 124343"). If field
|
||||
/// is not provided, static name configured by PayU will be used.
|
||||
pub fn with_statement_description<Description>(mut self, desc: Description) -> Self
|
||||
where
|
||||
Description: Into<String>,
|
||||
{
|
||||
self.statement_description = Some(String::from(desc.into().trim()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add url to which PayU will be able to send http request with payment
|
||||
/// status updates
|
||||
///
|
||||
/// All requests from PayU should receive 200 response!
|
||||
///
|
||||
/// See more [crate::res::Order::notify_url]
|
||||
pub fn with_notify_url<NotifyUrl>(mut self, notify_url: NotifyUrl) -> Self
|
||||
where
|
||||
NotifyUrl: Into<String>,
|
||||
{
|
||||
self.notify_url = Some(notify_url.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Address for redirecting the customer after payment is commenced. If
|
||||
/// the payment has not been authorized, error=501 parameter
|
||||
/// will be added. Please note that no decision regarding
|
||||
/// payment status should be made depending on the presence or
|
||||
/// lack of this parameter (to get payment status, wait for
|
||||
/// notification or retrieve order details).
|
||||
pub fn with_continue_url<ContinueUrl>(mut self, continue_url: ContinueUrl) -> Self
|
||||
where
|
||||
ContinueUrl: Into<String>,
|
||||
{
|
||||
self.continue_url = Some(continue_url.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Section containing credit data. This information is not required,
|
||||
/// but it is strongly recommended to include it. Otherwise the
|
||||
/// buyer will be prompted to provide missing data on provider
|
||||
/// page when payment by Installments or Pay later.
|
||||
pub fn with_credit(mut self, credit: Credit) -> Self {
|
||||
self.credit = Some(credit);
|
||||
self
|
||||
}
|
||||
|
||||
/// URL to which web hook will be send. It's important to return 200 to
|
||||
/// all notifications.
|
||||
///
|
||||
/// All notifications are send as POST with JSON payload
|
||||
///
|
||||
/// Notifications are sent immediately after a payment status changes.
|
||||
/// If the notification is not received by the Shop application,
|
||||
/// it will be sent again in accordance with the table below:
|
||||
///
|
||||
/// | Attempt | Time |
|
||||
/// |---------|------|
|
||||
/// | 1 | immediately |
|
||||
/// | 2 | 1 minute |
|
||||
/// | 3 | 2 minutes |
|
||||
/// | 4 | 5 minutes |
|
||||
/// | 5 | 10 minutes |
|
||||
/// | 6 | 30 minutes |
|
||||
/// | 7 | 1 hour |
|
||||
/// | 8 | 2 hours |
|
||||
/// | 9 | 3 hours |
|
||||
/// | 10| 6 hours |
|
||||
/// | 11| 9 hours |
|
||||
/// | 12| 12 hours |
|
||||
/// | 13| 15 hours |
|
||||
/// | 14| 18 hours |
|
||||
/// | 15| 21 hours |
|
||||
/// | 16| 24 hours |
|
||||
/// | 17| 36 hours |
|
||||
/// | 18| 48 hours |
|
||||
/// | 19| 60 hours |
|
||||
/// | 20| 72 hours |
|
||||
pub fn notify_url(&self) -> &Option<String> {
|
||||
&self.notify_url
|
||||
}
|
||||
|
||||
/// Customer IP address from http request received from client
|
||||
pub fn customer_ip(&self) -> &String {
|
||||
&self.customer_ip
|
||||
}
|
||||
|
||||
pub fn merchant_pos_id(&self) -> MerchantPosId {
|
||||
self.merchant_pos_id
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &String {
|
||||
&self.description
|
||||
}
|
||||
|
||||
pub fn currency_code(&self) -> &String {
|
||||
&self.currency_code
|
||||
}
|
||||
|
||||
pub fn total_amount(&self) -> &Price {
|
||||
&self.total_amount
|
||||
}
|
||||
|
||||
pub fn buyer(&self) -> &Option<Buyer> {
|
||||
&self.buyer
|
||||
}
|
||||
|
||||
pub fn products(&self) -> &[Product] {
|
||||
&self.products
|
||||
}
|
||||
|
||||
pub fn order_create_date(&self) -> &Option<String> {
|
||||
&self.order_create_date
|
||||
}
|
||||
|
||||
pub(crate) fn with_merchant_pos_id(mut self, merchant_pos_id: MerchantPosId) -> Self {
|
||||
self.merchant_pos_id = merchant_pos_id;
|
||||
self
|
||||
}
|
||||
}
|
144
crates/payment_adapter_pay_u/src/res.rs
Normal file
144
crates/payment_adapter_pay_u/src/res.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use crate::model::*;
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateOrder {
|
||||
/// Http status as a text
|
||||
pub status: Status,
|
||||
/// Client should be redirected to this URI
|
||||
pub redirect_uri: String,
|
||||
/// This should be connected to your own order
|
||||
pub order_id: OrderId,
|
||||
/// This is YOUR_EXT_ORDER_ID
|
||||
pub ext_order_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RefundDetails {
|
||||
pub order_id: Option<String>,
|
||||
pub refund: Option<Refund>,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Refunds {
|
||||
pub refunds: Vec<Refund>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionPayMethod {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum CardProfile {
|
||||
Consumer,
|
||||
Business,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum CardClassification {
|
||||
Debit,
|
||||
Credit,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionCartData {
|
||||
/// // "543402******4014",
|
||||
pub card_number_masked: String,
|
||||
/// MC (MasterCard/Maestro), VS (Visa)
|
||||
/// Example; "MC"
|
||||
pub card_scheme: String,
|
||||
pub card_profile: CardProfile,
|
||||
pub card_classification: CardClassification,
|
||||
/// Example: "000"
|
||||
pub card_response_code: String,
|
||||
/// Example: "000 - OK"
|
||||
pub card_response_code_desc: String,
|
||||
/// Example: "5"
|
||||
pub card_eci_code: String,
|
||||
/// Example: "AY",
|
||||
pub card3ds_status: String,
|
||||
/// Example: "PL",
|
||||
pub card_bin_country: String,
|
||||
/// Example: "MCC0111LL1121"
|
||||
pub first_transaction_id: String,
|
||||
}
|
||||
|
||||
/// > Installment proposal on the Sandbox environment is not related to the
|
||||
/// > order amount and always returns data for 480 PLN.
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionCardInstallmentProposal {
|
||||
/// Example: "5aff3ba8-0c37-4da1-ba4a-4ff24bcc2eed"
|
||||
pub proposal_id: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionCart {
|
||||
pub cart_data: TransactionCartData,
|
||||
pub card_installment_proposal: TransactionCardInstallmentProposal,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Transaction {
|
||||
pub pay_method: TransactionPayMethod,
|
||||
pub payment_flow: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Transactions {
|
||||
pub transactions: Vec<Transaction>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Order {
|
||||
/// Example: "{orderId}",
|
||||
pub order_id: super::OrderId,
|
||||
/// Example: "358766",
|
||||
pub ext_order_id: Option<String>,
|
||||
/// Example: "2014-10-27T14:58:17.443+01:00",
|
||||
pub order_create_date: String,
|
||||
/// Example: "http://localhost/OrderNotify/",
|
||||
pub notify_url: Option<String>,
|
||||
/// Example: "127.0.0.1",
|
||||
pub customer_ip: String,
|
||||
/// Example: "145227",
|
||||
pub merchant_pos_id: String,
|
||||
/// Example: "New order",
|
||||
pub description: String,
|
||||
/// Example: "PLN",
|
||||
pub currency_code: String,
|
||||
/// Example: "3200",
|
||||
pub total_amount: String,
|
||||
/// Example: "NEW",
|
||||
pub status: String,
|
||||
/// Example: `[{"name":"Product1","unitPrice":"1000","quantity":"1"}]`
|
||||
pub products: Vec<super::Product>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrdersInfo {
|
||||
pub orders: Vec<Order>,
|
||||
pub status: Status,
|
||||
pub properties: Option<Vec<Prop>>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrderInfo {
|
||||
pub order: Order,
|
||||
pub status: Status,
|
||||
pub properties: Option<Vec<Prop>>,
|
||||
}
|
25
crates/payment_adapter_pay_u/src/serialize.rs
Normal file
25
crates/payment_adapter_pay_u/src/serialize.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
pub(crate) fn serialize_i32<S>(v: &i32, ser: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
ser.serialize_str(&format!("{v}"))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize_newtype<N: Display, S>(
|
||||
v: &N,
|
||||
ser: S,
|
||||
) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
ser.serialize_str(&format!("{v}"))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize_u32<S>(v: &u32, ser: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
ser.serialize_str(&format!("{v}"))
|
||||
}
|
@ -34,6 +34,8 @@ wapc = { version = "1.0.0", features = [] }
|
||||
wapc-codec = { version = "1.0.0" }
|
||||
wapc-pool = { version = "1.0.0" }
|
||||
wasmtime-provider = { version = "1.3.2", features = [] }
|
||||
reqwest = { version = "0.11.13", features = ["default", "json", "blocking"] }
|
||||
#pay_u = { path = "../../vendor/pay_u" }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
|
@ -1,7 +1,12 @@
|
||||
use std::fs::read_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use bincode::deserialize;
|
||||
use config::{AppConfig, UpdateConfig};
|
||||
use payment_adapter::{HttpMethod, HttpRequest};
|
||||
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||
use wapc::WasiParams;
|
||||
use wapc_pool::HostPoolBuilder;
|
||||
// use payment_adapter::{CreatePayment, PaymentAdapter, Status};
|
||||
|
||||
@ -43,7 +48,7 @@ async fn main() {
|
||||
)
|
||||
});
|
||||
for file in dir.filter_map(|r| r.map(|r| r.path()).ok()) {
|
||||
eprintln!("{:?}", file.extension());
|
||||
eprintln!("{:?}", file);
|
||||
if file.extension().and_then(|s| s.to_str()) != Some("wasm") {
|
||||
continue;
|
||||
}
|
||||
@ -51,6 +56,7 @@ async fn main() {
|
||||
let module = std::fs::read(&file).unwrap();
|
||||
let engine = wasmtime_provider::WasmtimeEngineProviderBuilder::new()
|
||||
.module_bytes(&module)
|
||||
.wasi_params(WasiParams::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
@ -59,7 +65,11 @@ async fn main() {
|
||||
.factory(move || {
|
||||
wapc::WapcHost::new(
|
||||
Box::new(engine.clone()),
|
||||
Some(Box::new(move |_a, _b, _c, _d, _e| Ok(vec![]))),
|
||||
Some(Box::new(
|
||||
move |_a, _binding, _namespace, msg_name, payload| {
|
||||
Ok(host_call(msg_name, payload)?)
|
||||
},
|
||||
)),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
@ -67,6 +77,18 @@ async fn main() {
|
||||
.build();
|
||||
|
||||
{
|
||||
let msg = config
|
||||
.lock()
|
||||
.payment()
|
||||
.providers
|
||||
.get("pay_u")
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
pool.call("init", wapc_codec::messagepack::serialize(msg).unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let msg = payment_adapter::CreatePayment {
|
||||
buyer: payment_adapter::Buyer {
|
||||
email: "email".to_string(),
|
||||
@ -105,3 +127,123 @@ async fn main() {
|
||||
// let mqtt_client = mqtt::start(config.clone(), db.clone()).await;
|
||||
// rpc::start(config, db, mqtt_client).await;
|
||||
}
|
||||
|
||||
fn host_call(name: &str, payload: &[u8]) -> Result<Vec<u8>, payment_adapter::Error> {
|
||||
match name {
|
||||
"http_req" => {
|
||||
let req: HttpRequest = match deserialize(payload) {
|
||||
Ok(req) => req,
|
||||
_ => return Ok(vec![]),
|
||||
};
|
||||
http_request(req)
|
||||
}
|
||||
_ => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
fn http_request(req: HttpRequest) -> Result<Vec<u8>, payment_adapter::Error> {
|
||||
let HttpRequest {
|
||||
method,
|
||||
url,
|
||||
headers,
|
||||
bearer_auth,
|
||||
body,
|
||||
} = req;
|
||||
let client = reqwest::blocking::ClientBuilder::default()
|
||||
.user_agent("curl/7.82.0")
|
||||
// .use_native_tls()
|
||||
// Do not follow redirect!
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.connection_verbose(true)
|
||||
.build()
|
||||
.expect("Failed to create client");
|
||||
|
||||
let headers = {
|
||||
let len = headers.len();
|
||||
headers
|
||||
.into_iter()
|
||||
.fold(HeaderMap::with_capacity(len), |mut m, (k, v)| {
|
||||
if let (Ok(v), Ok(k)) =
|
||||
(HeaderValue::from_str(v.as_str()), HeaderName::from_str(&k))
|
||||
{
|
||||
m.insert(k, v);
|
||||
}
|
||||
m
|
||||
})
|
||||
};
|
||||
|
||||
match (method, bearer_auth) {
|
||||
(HttpMethod::Get, Some(bearer)) => {
|
||||
let text = client
|
||||
.get(url)
|
||||
.headers(headers)
|
||||
.bearer_auth(bearer)
|
||||
.send()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?
|
||||
.text()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?;
|
||||
Ok(text.into_bytes())
|
||||
}
|
||||
(HttpMethod::Get, _) => {
|
||||
let text = client
|
||||
.get(url)
|
||||
.headers(headers)
|
||||
.send()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?
|
||||
.text()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?;
|
||||
Ok(text.into_bytes())
|
||||
}
|
||||
(HttpMethod::Post, Some(bearer)) => {
|
||||
let body = body.unwrap_or_default();
|
||||
let len = body.len();
|
||||
let body = std::io::Cursor::new(body);
|
||||
let body = reqwest::blocking::Body::sized(body, len as u64);
|
||||
let text = client
|
||||
.post(url)
|
||||
.headers(headers)
|
||||
.bearer_auth(bearer)
|
||||
.body(body)
|
||||
.send()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?
|
||||
.text()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?;
|
||||
Ok(text.into_bytes())
|
||||
}
|
||||
(HttpMethod::Post, None) => {
|
||||
let text = client
|
||||
.post(url)
|
||||
.headers(headers)
|
||||
.body(body.unwrap_or_default())
|
||||
.send()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?
|
||||
.text()
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
payment_adapter::Error::HttpFailed
|
||||
})?;
|
||||
Ok(text.into_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,109 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use model::*;
|
||||
|
||||
// use crate::{Buyer, Error, PayUClient, Result};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Buyer {
|
||||
/// Required customer e-mail
|
||||
pub email: String,
|
||||
/// Required customer phone number
|
||||
pub phone: String,
|
||||
/// Required customer first name
|
||||
pub first_name: String,
|
||||
/// Required customer last name
|
||||
pub last_name: String,
|
||||
/// Required customer language
|
||||
pub language: String,
|
||||
}
|
||||
|
||||
impl From<Buyer> for pay_u::Buyer {
|
||||
fn from(b: Buyer) -> Self {
|
||||
pay_u::Buyer::new(b.email, b.phone, b.first_name, b.last_name, b.language)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Product {
|
||||
pub id: ProductId,
|
||||
pub name: String,
|
||||
pub unit_price: Price,
|
||||
pub quantity_unit: QuantityUnit,
|
||||
pub quantity: Quantity,
|
||||
}
|
||||
|
||||
impl From<Product> for pay_u::Product {
|
||||
fn from(p: Product) -> Self {
|
||||
pay_u::Product::new(p.name, **p.unit_price, **p.quantity as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreatePayment {
|
||||
pub client: PayUClient,
|
||||
pub buyer: Buyer,
|
||||
pub customer_ip: String,
|
||||
pub currency: String,
|
||||
pub description: String,
|
||||
pub cart_products: Vec<model::Product>,
|
||||
pub items: HashMap<ProductId, (Quantity, QuantityUnit)>,
|
||||
pub order_ext_id: String,
|
||||
pub notify_uri: String,
|
||||
pub continue_uri: String,
|
||||
}
|
||||
|
||||
impl CreatePayment {
|
||||
pub(crate) async fn create_payment(self) -> Result<(String, ExtOrderId)> {
|
||||
let CreatePayment {
|
||||
client,
|
||||
buyer,
|
||||
customer_ip,
|
||||
currency,
|
||||
description,
|
||||
cart_products,
|
||||
mut items,
|
||||
order_ext_id,
|
||||
notify_uri,
|
||||
continue_uri,
|
||||
} = self;
|
||||
|
||||
let pay_u::res::CreateOrder {
|
||||
status: _,
|
||||
redirect_uri,
|
||||
order_id,
|
||||
ext_order_id: _,
|
||||
} = client
|
||||
.lock()
|
||||
.create_order(
|
||||
pay_u::req::OrderCreate::build(buyer.into(), customer_ip, currency, description)
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::InvalidOrder
|
||||
})?
|
||||
.with_products(cart_products.into_iter().map(|p| {
|
||||
pay_u::Product::new(
|
||||
p.name.to_string(),
|
||||
**p.price,
|
||||
items
|
||||
.remove(&p.id)
|
||||
.map(|(quantity, _)| **quantity as u32)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}))
|
||||
.with_ext_order_id(order_ext_id)
|
||||
.with_notify_url(notify_uri)
|
||||
.with_continue_url(continue_uri),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})?;
|
||||
Ok((redirect_uri, ExtOrderId::new(order_id.0)))
|
||||
}
|
||||
}
|
11
vendor/pay_u/src/lib.rs
vendored
11
vendor/pay_u/src/lib.rs
vendored
@ -1295,17 +1295,6 @@ impl Client {
|
||||
"https://secure.payu.com/api/v2_1"
|
||||
}
|
||||
}
|
||||
|
||||
fn build_client() -> reqwest::Client {
|
||||
reqwest::ClientBuilder::default()
|
||||
.user_agent("curl/7.82.0")
|
||||
// .use_native_tls()
|
||||
// Do not follow redirect!
|
||||
.redirect(redirect::Policy::none())
|
||||
.connection_verbose(true)
|
||||
.build()
|
||||
.expect("Failed to create client")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user