Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set custom Moonpay redirectURL #1040

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libs/sdk-bindings/src/breez_sdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ dictionary ReceiveOnchainRequest {
dictionary BuyBitcoinRequest {
BuyBitcoinProvider provider;
OpeningFeeParams? opening_fee_params = null;
string? redirect_url = null;
};

dictionary BuyBitcoinResponse {
Expand Down
7 changes: 6 additions & 1 deletion libs/sdk-core/src/breez_services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1349,7 +1349,11 @@ impl BreezServices {
})
.await?;
let url = match req.provider {
Moonpay => self.moonpay_api.buy_bitcoin_url(&swap_info).await?,
Moonpay => {
self.moonpay_api
.buy_bitcoin_url(&swap_info, req.redirect_url)
.await?
}
};

Ok(BuyBitcoinResponse {
Expand Down Expand Up @@ -3094,6 +3098,7 @@ pub(crate) mod tests {
.buy_bitcoin(BuyBitcoinRequest {
provider: BuyBitcoinProvider::Moonpay,
opening_fee_params: None,
redirect_url: None,
})
.await?
.url;
Expand Down
3 changes: 3 additions & 0 deletions libs/sdk-core/src/bridge_generated.io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ impl Wire2Api<BuyBitcoinRequest> for wire_BuyBitcoinRequest {
BuyBitcoinRequest {
provider: self.provider.wire2api(),
opening_fee_params: self.opening_fee_params.wire2api(),
redirect_url: self.redirect_url.wire2api(),
}
}
}
Expand Down Expand Up @@ -1134,6 +1135,7 @@ impl Wire2Api<Vec<u8>> for *mut wire_uint_8_list {
pub struct wire_BuyBitcoinRequest {
provider: i32,
opening_fee_params: *mut wire_OpeningFeeParams,
redirect_url: *mut wire_uint_8_list,
}

#[repr(C)]
Expand Down Expand Up @@ -1490,6 +1492,7 @@ impl NewWithNullPtr for wire_BuyBitcoinRequest {
Self {
provider: Default::default(),
opening_fee_params: core::ptr::null_mut(),
redirect_url: core::ptr::null_mut(),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions libs/sdk-core/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,11 @@ pub struct ReceiveOnchainRequest {
pub struct BuyBitcoinRequest {
pub provider: BuyBitcoinProvider,
pub opening_fee_params: Option<OpeningFeeParams>,

/// The optional URL to redirect to after completing the buy.
///
/// See <https://dev.moonpay.com/docs/on-ramp-configure-user-journey-params>
pub redirect_url: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
55 changes: 50 additions & 5 deletions libs/sdk-core/src/moonpay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,19 @@ pub(crate) fn moonpay_config() -> MoonPayConfig {
}
}

async fn create_moonpay_url(wallet_address: &str, max_amount: &str) -> Result<Url> {
async fn create_moonpay_url(
wallet_address: &str,
max_amount: &str,
redirect_url: Option<String>,
) -> Result<Url> {
let config = moonpay_config();
let url = Url::parse_with_params(
&config.base_url,
&[
("apiKey", &config.api_key),
("currencyCode", &config.currency_code),
("colorCode", &config.color_code),
("redirectURL", &config.redirect_url),
("redirectURL", &redirect_url.unwrap_or(config.redirect_url)),
("enabledPaymentMethods", &config.enabled_payment_methods),
("walletAddress", &wallet_address.to_string()),
("maxQuoteCurrencyAmount", &max_amount.to_string()),
Expand All @@ -47,16 +51,25 @@ async fn create_moonpay_url(wallet_address: &str, max_amount: &str) -> Result<Ur

#[tonic::async_trait]
pub(crate) trait MoonPayApi: Send + Sync {
async fn buy_bitcoin_url(&self, swap_info: &SwapInfo) -> Result<String>;
async fn buy_bitcoin_url(
&self,
swap_info: &SwapInfo,
redirect_url: Option<String>,
) -> Result<String>;
}

#[tonic::async_trait]
impl MoonPayApi for BreezServer {
async fn buy_bitcoin_url(&self, swap_info: &SwapInfo) -> Result<String> {
async fn buy_bitcoin_url(
&self,
swap_info: &SwapInfo,
redirect_url: Option<String>,
) -> Result<String> {
let config = moonpay_config();
let url = create_moonpay_url(
swap_info.bitcoin_address.as_str(),
format!("{:.8}", swap_info.max_allowed_deposit as f64 / 100000000.0).as_str(),
redirect_url,
)
.await?;
let mut signer = self.get_signer_client().await;
Expand Down Expand Up @@ -84,7 +97,7 @@ pub(crate) mod tests {
let max_amount = "a max amount";
let config = moonpay_config();

let url = create_moonpay_url(wallet_address, max_amount).await?;
let url = create_moonpay_url(wallet_address, max_amount, None).await?;

let query_pairs = url.query_pairs().into_owned().collect::<HashMap<_, _>>();
assert_eq!(url.host_str(), Some("buy.moonpay.io"));
Expand All @@ -107,4 +120,36 @@ pub(crate) mod tests {
);
Ok(())
}

#[tokio::test]
async fn test_sign_moonpay_url_with_redirect() -> Result<(), Box<dyn std::error::Error>> {
let wallet_address = "a wallet address";
let max_amount = "a max amount";
let redirect_url = "https://test.moonpay.url/receipt".to_string();
let config = moonpay_config();

let url =
create_moonpay_url(wallet_address, max_amount, Some(redirect_url.clone())).await?;

let query_pairs = url.query_pairs().into_owned().collect::<HashMap<_, _>>();
assert_eq!(url.host_str(), Some("buy.moonpay.io"));
assert_eq!(url.path(), "/");
assert_eq!(query_pairs.get("apiKey"), Some(&config.api_key));
assert_eq!(query_pairs.get("currencyCode"), Some(&config.currency_code));
assert_eq!(query_pairs.get("colorCode"), Some(&config.color_code));
assert_eq!(query_pairs.get("redirectURL"), Some(&redirect_url));
assert_eq!(
query_pairs.get("enabledPaymentMethods"),
Some(&config.enabled_payment_methods),
);
assert_eq!(
query_pairs.get("walletAddress"),
Some(&String::from(wallet_address))
);
assert_eq!(
query_pairs.get("maxQuoteCurrencyAmount"),
Some(&String::from(max_amount)),
);
Ok(())
}
}
6 changes: 5 additions & 1 deletion libs/sdk-core/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,11 @@ impl FiatAPI for MockBreezServer {

#[tonic::async_trait]
impl MoonPayApi for MockBreezServer {
async fn buy_bitcoin_url(&self, swap_info: &SwapInfo) -> Result<String> {
async fn buy_bitcoin_url(
&self,
swap_info: &SwapInfo,
_redirect_url: Option<String>,
) -> Result<String> {
Ok(format!(
"https://mock.moonpay?wa={}&ma={}",
swap_info.bitcoin_address.as_str(),
Expand Down
1 change: 1 addition & 0 deletions libs/sdk-flutter/ios/Classes/bridge_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ typedef struct wire_ReceiveOnchainRequest {
typedef struct wire_BuyBitcoinRequest {
int32_t provider;
struct wire_OpeningFeeParams *opening_fee_params;
struct wire_uint_8_list *redirect_url;
} wire_BuyBitcoinRequest;

typedef struct wire_RedeemOnchainFundsRequest {
Expand Down
9 changes: 9 additions & 0 deletions libs/sdk-flutter/lib/bridge_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,15 @@ class BuyBitcoinRequest {
final BuyBitcoinProvider provider;
final OpeningFeeParams? openingFeeParams;

/// The optional URL to redirect to after completing the buy.
///
/// See <https://dev.moonpay.com/docs/on-ramp-configure-user-journey-params>
final String? redirectUrl;

const BuyBitcoinRequest({
required this.provider,
this.openingFeeParams,
this.redirectUrl,
});
}

Expand Down Expand Up @@ -4757,6 +4763,7 @@ class BreezSdkCorePlatform extends FlutterRustBridgeBase<BreezSdkCoreWire> {
void _api_fill_to_wire_buy_bitcoin_request(BuyBitcoinRequest apiObj, wire_BuyBitcoinRequest wireObj) {
wireObj.provider = api2wire_buy_bitcoin_provider(apiObj.provider);
wireObj.opening_fee_params = api2wire_opt_box_autoadd_opening_fee_params(apiObj.openingFeeParams);
wireObj.redirect_url = api2wire_opt_String(apiObj.redirectUrl);
}

void _api_fill_to_wire_check_message_request(CheckMessageRequest apiObj, wire_CheckMessageRequest wireObj) {
Expand Down Expand Up @@ -6761,6 +6768,8 @@ final class wire_BuyBitcoinRequest extends ffi.Struct {
external int provider;

external ffi.Pointer<wire_OpeningFeeParams> opening_fee_params;

external ffi.Pointer<wire_uint_8_list> redirect_url;
}

final class wire_RedeemOnchainFundsRequest extends ffi.Struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,19 @@ fun asBuyBitcoinRequest(buyBitcoinRequest: ReadableMap): BuyBitcoinRequest? {
} else {
null
}
val redirectUrl = if (hasNonNullKey(buyBitcoinRequest, "redirectUrl")) buyBitcoinRequest.getString("redirectUrl") else null
return BuyBitcoinRequest(
provider,
openingFeeParams,
redirectUrl,
)
}

fun readableMapOf(buyBitcoinRequest: BuyBitcoinRequest): ReadableMap =
readableMapOf(
"provider" to buyBitcoinRequest.provider.name.lowercase(),
"openingFeeParams" to buyBitcoinRequest.openingFeeParams?.let { readableMapOf(it) },
"redirectUrl" to buyBitcoinRequest.redirectUrl,
)

fun asBuyBitcoinRequestList(arr: ReadableArray): List<BuyBitcoinRequest> {
Expand Down
Loading
Loading