Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ of this software and associated documentation files (the "Software"), to deal
import com.shopify.checkoutsheetkit.lifecycleevents.CheckoutStartEvent;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutAddressChangeStart;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutAddressChangeStartEvent;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutSubmitStart;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutSubmitStartEvent;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -351,6 +353,26 @@ public void onAddressChangeStart(@NonNull CheckoutAddressChangeStart event) {
}
}

@Override
public void onSubmitStart(@NonNull CheckoutSubmitStart event) {
try {
CheckoutSubmitStartEvent params = event.getParams();
Map<String, Object> eventData = new HashMap<>();

eventData.put("id", event.getId());
eventData.put("type", "submitStart");
eventData.put("cart", params.getCart());

Map<String, Object> checkoutData = new HashMap<>();
checkoutData.put("id", params.getCheckout().getId());
eventData.put("checkout", checkoutData);

sendEvent("onSubmitStart", serializeToWritableMap(eventData));
} catch (Exception e) {
Log.e(TAG, "Error processing submit start event", e);
}
}

@Override
public void onLinkClick(@NonNull Uri uri) {
WritableMap params = Arguments.createMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
events.put("onCancel", createEventMap("onCancel"));
events.put("onLinkClick", createEventMap("onLinkClick"));
events.put("onAddressChangeStart", createEventMap("onAddressChangeStart"));
events.put("onSubmitStart", createEventMap("onSubmitStart"));
return events;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ of this software and associated documentation files (the "Software"), to deal
import com.shopify.checkoutsheetkit.lifecycleevents.CheckoutStartEvent;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutAddressChangeStart;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutAddressChangeStartEvent;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutSubmitStart;
import com.shopify.checkoutsheetkit.rpc.events.CheckoutSubmitStartEvent;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
Expand Down Expand Up @@ -172,6 +174,31 @@ public void onAddressChangeStart(@NonNull CheckoutAddressChangeStart event) {
}
}

@Override
public void onSubmitStart(@NonNull CheckoutSubmitStart event) {
try {
CheckoutSubmitStartEvent params = event.getParams();
if (params == null) {
Log.e("ShopifyCheckoutSheetKit", "Submit start event has null params");
return;
}

Map<String, Object> eventData = new HashMap<>();
eventData.put("id", event.getId());
eventData.put("type", "submitStart");
eventData.put("cart", params.getCart());

Map<String, Object> checkoutData = new HashMap<>();
checkoutData.put("id", params.getCheckout().getId());
eventData.put("checkout", checkoutData);

String data = mapper.writeValueAsString(eventData);
sendEventWithStringData("submitStart", data);
} catch (IOException e) {
Log.e("ShopifyCheckoutSheetKit", "Error processing submit start event", e);
}
}

// Private

private Map<String, Object> populateErrorDetails(CheckoutException error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class RCTCheckoutWebView: UIView {
@objc var onLinkClick: RCTBubblingEventBlock?
@objc var onAddressChangeStart: RCTBubblingEventBlock?
@objc var onPaymentChangeIntent: RCTBubblingEventBlock?
@objc var onSubmitStart: RCTBubblingEventBlock?

override init(frame: CGRect) {
super.init(frame: frame)
Expand Down Expand Up @@ -324,4 +325,30 @@ extension RCTCheckoutWebView: CheckoutDelegate {

onPaymentChangeIntent?(eventData)
}

/// Called when the buyer attempts to submit the checkout.
///
/// This event is only emitted when native payment delegation is configured
/// for the authenticated app.
///
/// - Parameter event: The submit start event containing:
/// - id: Unique identifier for responding to the event
/// - cart: Current cart state
/// - checkout: Checkout session information
func checkoutDidStartSubmit(event: CheckoutSubmitStart) {
guard let id = event.id else { return }

self.events.set(key: id, event: event)

let cartJSON = ShopifyEventSerialization.encodeToJSON(from: event.params.cart)

onSubmitStart?([
"id": event.id,
"type": "submitStart",
"cart": cartJSON,
"checkout": [
"id": event.params.checkout.id
],
])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ internal enum ShopifyEventSerialization {
]
}

/**
* Converts a CheckoutSubmitStart to a React Native compatible dictionary.
*/
static func serialize(checkoutSubmitStart event: CheckoutSubmitStart) -> [String: Any] {
return [
"id": event.id as Any,
"type": "submitStart",
"cart": encodeToJSON(from: event.params.cart),
"checkout": [
"id": event.params.checkout.id
]
]
}

static func serialize(clickEvent url: URL) -> [String: URL] {
return ["url": url]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ @interface RCT_EXTERN_MODULE (RCTCheckoutWebViewManager, RCTViewManager)
*/
RCT_EXPORT_VIEW_PROPERTY(onPaymentChangeIntent, RCTBubblingEventBlock)

/**
* Emitted when the buyer attempts to submit the checkout
*/
RCT_EXPORT_VIEW_PROPERTY(onSubmitStart, RCTBubblingEventBlock)

/**
* Emitted when a link is clicked
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
}

override func supportedEvents() -> [String]! {
return ["close", "complete", "start", "error", "addressChangeStart"]
return ["close", "complete", "start", "error", "addressChangeStart", "submitStart"]
}

override func startObserving() {
Expand Down Expand Up @@ -82,6 +82,12 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
}
}

func checkoutDidStartSubmit(event: CheckoutSubmitStart) {
if hasListeners {
sendEvent(withName: "submitStart", body: ShopifyEventSerialization.serialize(checkoutSubmitStart: event))
}
}

func shouldRecoverFromError(error: CheckoutError) -> Bool {
return error.isRecoverable
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ import type {
CheckoutException,
} from '..';
import {useCheckoutEvents} from '../CheckoutEventProvider';
import type {CheckoutAddressChangeStart, CheckoutStartEvent} from '../events';
import type {
CheckoutAddressChangeStart,
CheckoutStartEvent,
CheckoutSubmitStart
} from '../events';

export interface CheckoutProps {
/**
Expand Down Expand Up @@ -81,6 +85,14 @@ export interface CheckoutProps {
*/
onAddressChangeStart?: (event: CheckoutAddressChangeStart) => void;

/**
* Called when the buyer attempts to submit the checkout.
*
* Note: This callback is only invoked when native payment delegation is configured
* for the authenticated app.
*/
onSubmitStart?: (event: CheckoutSubmitStart) => void;

/**
* Style for the webview container
*/
Expand Down Expand Up @@ -110,6 +122,7 @@ interface NativeCheckoutWebViewProps {
onCancel?: () => void;
onLinkClick?: (event: {nativeEvent: {url: string}}) => void;
onAddressChangeStart?: (event: {nativeEvent: CheckoutAddressChangeStart}) => void;
onSubmitStart?: (event: {nativeEvent: CheckoutSubmitStart}) => void;
}

const RCTCheckoutWebView =
Expand Down Expand Up @@ -167,6 +180,7 @@ export const Checkout = forwardRef<CheckoutRef, CheckoutProps>(
onCancel,
onLinkClick,
onAddressChangeStart,
onSubmitStart,
style,
testID,
},
Expand Down Expand Up @@ -239,6 +253,16 @@ export const Checkout = forwardRef<CheckoutRef, CheckoutProps>(
[onAddressChangeStart],
);

const handleSubmitStart = useCallback<
Required<NativeCheckoutWebViewProps>['onSubmitStart']
>(
(event: {nativeEvent: CheckoutSubmitStart}) => {
if (!event.nativeEvent) return;
onSubmitStart?.(event.nativeEvent);
},
[onSubmitStart],
);

const reload = useCallback(() => {
if (!webViewRef.current) {
return;
Expand Down Expand Up @@ -273,6 +297,7 @@ export const Checkout = forwardRef<CheckoutRef, CheckoutProps>(
onCancel={handleCancel}
onLinkClick={handleLinkClick}
onAddressChangeStart={handleAddressChangeStart}
onSubmitStart={handleSubmitStart}
/>
);
},
Expand Down
73 changes: 69 additions & 4 deletions modules/@shopify/checkout-sheet-kit/src/events.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ export interface CheckoutResponseError {

/**
* Mailing address input for delivery.
* Aligns with Storefront API MailingAddressInput.
*
* @see https://shopify.dev/docs/api/storefront/latest/input-objects/MailingAddressInput
*/
Expand Down Expand Up @@ -428,7 +427,6 @@ export interface CartDeliveryAddressInput {

/**
* Delivery-related fields for the cart.
* Aligns with Storefront API CartDeliveryInput.
*
* @see https://shopify.dev/docs/api/storefront/latest/input-objects/CartDeliveryInput
*/
Expand All @@ -441,7 +439,6 @@ export interface CartDeliveryInput {

/**
* The customer associated with the cart.
* Aligns with Storefront API CartBuyerIdentityInput.
*
* @see https://shopify.dev/docs/api/storefront/latest/input-objects/CartBuyerIdentityInput
*/
Expand Down Expand Up @@ -523,7 +520,7 @@ export interface CheckoutAddressChangeStart {
*/
export interface CheckoutAddressChangeStartResponse {
/**
* Updated cart input with the delivery address to set.
* Updated cart input with the delivery addresses to set.
*/
cart?: CartInput;
/**
Expand All @@ -548,3 +545,71 @@ export interface CheckoutPaymentChangeIntent {
brand: string;
};
}

/**
* Checkout session information.
*/
export interface CheckoutSession {
/** Globally unique identifier for the checkout session */
id: string;
}

/**
* Event emitted when the buyer attempts to submit the checkout.
*
* This event is only emitted when native payment delegation is configured
* for the authenticated app.
*/
export interface CheckoutSubmitStart {
/**
* Unique identifier for this event instance.
* Use this ID with the CheckoutEventProvider to respond to the event.
*/
id: string;

/**
* The event type identifier
*/
type: 'submitStart';

/**
* The current cart state when the event was emitted.
*/
cart: Cart;

/**
* The checkout session information.
*/
checkout: CheckoutSession;
}

/**
* Payment token input for delegated payment processing.
*/
export interface PaymentTokenInput {
token: string;
tokenType: string;
tokenProvider: string;
}

/**
* Response payload for CheckoutSubmitStart event.
* Use with CheckoutEventProvider.respondToEvent() or useShopifyEvent().respondWith()
*
* Note: This response is only used when native payment delegation is enabled
* for the authenticated app.
*/
export interface CheckoutSubmitStartResponse {
/**
* Optional payment token information for delegated payment processing.
*/
payment?: PaymentTokenInput;
/**
* Updated cart input with delivery addresses and optional buyer identity.
*/
cart?: CartInput;
/**
* Optional array of errors if the submission failed.
*/
errors?: CheckoutResponseError[];
}
12 changes: 11 additions & 1 deletion modules/@shopify/checkout-sheet-kit/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO
*/

import type {EmitterSubscription} from 'react-native';
import type {CheckoutCompleteEvent, CheckoutStartEvent} from './events';
import type {CheckoutCompleteEvent, CheckoutStartEvent, CheckoutSubmitStart} from './events';
import type {CheckoutException} from './errors';

export type Maybe<T> = T | undefined;
Expand Down Expand Up @@ -153,6 +153,7 @@ export type CheckoutEvent =
| 'start'
| 'error'
| 'addressChangeStart'
| 'submitStart'
| 'geolocationRequest';

export interface GeolocationRequestEvent {
Expand All @@ -173,13 +174,17 @@ export type CheckoutStartEventCallback = (
export type CheckoutAddressChangeStartCallback = (
event: CheckoutAddressChangeStart,
) => void;
export type CheckoutSubmitStartCallback = (
event: CheckoutSubmitStart,
) => void;

export type CheckoutEventCallback =
| CloseEventCallback
| CheckoutExceptionCallback
| CheckoutCompleteEventCallback
| CheckoutStartEventCallback
| CheckoutAddressChangeStartCallback
| CheckoutSubmitStartCallback
| GeolocationRequestEventCallback;

/**
Expand Down Expand Up @@ -263,6 +268,11 @@ function addEventListener(
callback: CheckoutAddressChangeStartCallback,
): Maybe<EmitterSubscription>;

function addEventListener(
event: 'submitStart',
callback: CheckoutSubmitStartCallback,
): Maybe<EmitterSubscription>;

function addEventListener(
event: 'error',
callback: CheckoutExceptionCallback,
Expand Down
Loading
Loading