Integrating stripe react


#1

can anyone help me with integrating stripe into my reason app
currently i have components like this but i just get a blank white screen.
also it talks about loading the stripe.js into the page where is this loaded? since im trying to load a module with the elements nested inside, im new to reason and i dont understand how to do the react interop, even tho ive read the guide many times
react scripts im using are here https://github.com/stripe/react-stripe-elements

checkout.re

 type state =
 | LOADING
 | ERROR
 | CHECKOUT

type action =
 | FAILEDTOFETCH
 | CHECKOUT

let reducer = (action, _state) =>
  switch(action) {
  | FAILEDTOFETCH => ReasonReact.Update(ERROR)
  | CHECKOUT =>      ReasonReact.Update(CHECKOUT)

 };

let component = ReasonReact.reducerComponent("Checkout");

let make = children => {
  ...component,
  initialState: () => LOADING,
  reducer,
  didMount: self => {
  self.send(CHECKOUT);
  },
  render: (self) =>
  switch(self.state){
  | ERROR => <div> ( ReasonReact.string("An Error Occured !!") ) </div>
  | LOADING => <div className=Styles.app>

               <div> ( ReasonReact.string("Loading... ") ) </div>

               </div>
   | CHECKOUT =>      <div className=Styles.app>

                      <div> ( ReasonReact.string("Checkout goes here") ) </div>
                      <Stripe />

                      </div>
  }
};

stripe.re

[@bs.module] external inject: ReasonReact.reactClass = "../node_modules/react-stripe-elements/lib/components/inject";

[@bs.module] external elements: ReasonReact.reactClass = "../node_modules/react-stripe-elements/lib/components/Elements";

[@bs.module] external element: ReasonReact.reactClass = "../node_modules/react-stripe-elements/lib/components/Element";

[@bs.module] external provider: ReasonReact.reactClass = "../node_modules/react-stripe-elements/lib/components/Provider";

[@bs.module] external paymentrequestbuttonelement: ReasonReact.reactClass = "../node_modules/react-stripe-elements/lib/components/PaymentRequestButtonElement";

let make = (children) =>
  ReasonReact.wrapJsForReason(
    ~reactClass=elements,
    ~props=Js.Obj.empty(),
    children,
  );

#2

Hi, I don’t have a full answer for you, but I think I know some of the issues.

For loading Stripe.js, that means just including this in your index.html. Preferably, you’d include it before your index.js include.

So for example:

<body>
  <div id="root"></div>
  <script src="https://js.stripe.com/v3/"></script>
  <script src="../build/Index.js"></script>
</body>

For your bindings, I think you’re running into this issue (https://github.com/reasonml/reason-react/blob/master/docs/element-type-is-invalid.md).

I’m currently implementing the bindings just for practice, if I get it done I’ll link it to ya!

You’ll probably need this to for injection: https://jaredforsyth.com/posts/advanced-reasonreact-hider-order-components/


#3

heres what ive come up with so far i still dont have the hoc built am i on the right tracl


#4

You are, but the HOC is important.

For HOC I believe you need to use children, which will allow you to do something like this (exposing the stripe prop basically):

        <Elements>
          ...<Inject> ...{(~stripe) => <CardElement stripe />} </Inject>
        </Elements>

The prop has to be explicit since this isn’t JavaScript.

For the Inject code, something like this works:

type hoc = ReasonReact.reactClass => ReasonReact.reactClass;

[@bs.module "react-stripe-elements/lib/components/inject"]
external inject: hoc = "default";

type children = (~stripe: stripe) => ReasonReact.reactElement;

let component = ReasonReact.statelessComponent("Inject");

let make' = (~stripe: stripe, children: children) => {
  ...component,
  render: _self => children(~stripe),
};

let jsComponent =
  ReasonReact.wrapReasonForJs(
    ~component,
    (
      props: {
        .
        "stripe": stripe,
        "children": children,
      },
    ) =>
    make'(~stripe=props##stripe, props##children)
  );

let enhanced = inject(jsComponent);

let make = (children: children) =>
  ReasonReact.wrapJsForReason(
    ~reactClass=enhanced,
    ~props=Js.Obj.empty(),
    children,
  );

You’ll then have to define the external functions using Reason conventions, so for example:

[@bs.deriving abstract]
type stripe;

[@bs.send]
external createToken:
  (stripe, option(createTokenArgs)) => Js.Promise.t(createTokenResponse) =
  "createToken";

#5

that makes things a lot clearer thx, ima give it a whirl and see how far i get!!


#6

im not sure how to use the

 <Elements>
          ...<Inject> ...{(~stripe) => <CardElement stripe />} </Inject>
  </Elements>

do i need to include this in the make of my checkout module? i read the included and while iot made since it doesnt cover actual implentation, do i need to make an Elements module that imports the external element ive exported from the
[@bs.module “react-stripe-elements/lib/components/elements”]
external elements = “default”;
the documentationm on interop is fairly high level for me and thus confusing. is the pattern that has been outlied the same one i use for all the exports in this particular lib?

i created an Inject.re module that looks like

type hoc = ReasonReact.reactClass => ReasonReact.reactClass;

[@bs.deriving abstract]
type stripe;

[@bs.module "react-stripe-elements/lib/components/inject"]
external inject: hoc = "default";

type children = (~stripe: stripe) => ReasonReact.reactElement;

let component = ReasonReact.statelessComponent("Inject");

let make = (~stripe: stripe, children: children) => {
  ...component,
  render: _self => children(~stripe),
};

let jsComponent =
  ReasonReact.wrapReasonForJs(
    ~component,
    (
      props: {
        .
        "stripe": stripe,
        "children": children,
      },
    ) =>
    make(~stripe=props##stripe, props##children)
  );

let enhanced = inject(jsComponent);

let make = (children: children) =>
  ReasonReact.wrapJsForReason(
    ~reactClass=enhanced,
    ~props=Js.Obj.empty(),
    children,
  );

#7

Elements is a convenient element in react-stripe-elements. I suggest defining a make for it since you already have

[@bs.module "../node_modules/react-stripe-elements/lib/components/Elements"] external elements: ReasonReact.reactClass = "default";

You would have to make a module for it and a make function in order for the interop to work.

/* Elements.re */
[@bs.module "react-stripe-elements/lib/components/Elements"]
external elements: ReasonReact.reactClass = "default";

let make = (children: ReasonReact.reactElement) =>
  ReasonReact.wrapJsForReason(
    ~reactClass=elements,
    ~props=Js.Obj.empty(),
    children,
  );

This is what you want. You can define this for the other elements too.


#8

ok i understand that and mostly works but im trying to create the card element, which takes a prop im getting the error
App.Card.children =>
ReasonReact.component(ReasonReact.stateless, ReasonReact.noRetainedProps,
ReasonReact.actionless)
It only accepts 1 argument; here, it’s called with more.

heres my class

type hoc = ReasonReact.reactClass => ReasonReact.reactClass;

[@bs.deriving abstract]
type stripe;

[@bs.module "react-stripe-elements/lib/components/Element"]
external element: hoc = "default";


type children = (~stripe: stripe) => ReasonReact.reactElement;

let component = ReasonReact.statelessComponent("Card");

let make = (~stripe: stripe, children: children) => {
  ...component,
  render: _self => children(~stripe),
};

let jsComponent =
  ReasonReact.wrapReasonForJs(
    ~component,
    (
      props: {
        .
        "stripe": stripe,
        "children": children,
      },
    ) =>
    make(~stripe=props##stripe, props##children)
  );

let enhanced = element(jsComponent);

let make = (children: children) =>
  ReasonReact.wrapJsForReason(
    ~reactClass=enhanced,
    ~props=Js.Obj.empty(),
    children,
  );

im trying to use it like
< Elements>
< Inject> …{(~stripe) => < Card stripe />} < /Inject>
< /Elements>