mercredi 15 novembre 2023

How do I implement a paypal checkout button, given the design patterns that I used in my application?

I followed a long tutorial, and built my application side by side using the design patterns from the turorial. I am now stuck, as I don't know how to implement paypal within the scheme I'm using. I used rxjs and observables to build the app, and admittedly I don't really understand them well. Sorry if this is vague, I don;t know what else to do. The paypal implementation is as follows...

This is the code I do not understand how to implement.

TS file

export class DeposittestComponent implements OnInit {
  

  PAYPAL_CLIENT_ID: string = '##################';

  myAmount: any

@ViewChild('paypalRef', { static: true }) private paypalRef!: ElementRef;

ngOnInit(): void {

  this.renderPaypalButton();

  }

  renderPaypalButton() {
    let orderRef: string | null = null;


    loadScript({ "clientId": this.PAYPAL_CLIENT_ID, currency: 'USD' })
      .then((loadedPaypal) => {
        if (loadedPaypal) {
          const paypal: PayPalNamespace = loadedPaypal;
          paypal.Buttons({
            style: {
              layout: 'vertical',
              color: 'blue',
              shape: 'rect',
              label: 'paypal',
            },
            createOrder: (data, actions) => {
              const postData = {
                amount: this.myAmount
              };
              return fetch(AppSettings.API_ENDPOINT + '/user/create_paypal_transaction', {
                method: "post",
                headers: {
                  'Accept': 'application/json, text/plain, */*',
                  'Content-Type': 'application/json'
                },
                body: JSON.stringify(postData)
              })
                .then((response) => response.json())
                .then((response) => {
                  orderRef = response.orderRef;
                  return response.id;
                });
            },
            onApprove: (data, actions) => {
              return fetch(AppSettings.API_ENDPOINT + `/user/orders/${data.orderID}/capture`, {
                method: "post",
                headers: {
                  'Accept': 'application/json, text/plain, */*',
                  'Content-Type': 'application/json',
                },
              })
                .then((response) => response.json())
                .then((orderData) => {
                  const errorDetail = Array.isArray(orderData.details) && orderData.details[0];

                  if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
                    return actions.restart();
                  }
                  const transaction = orderData.purchase_units[0].payments.captures[0];

                  this.paypalRedirect(transaction.status, orderRef);
                });
            },
            onCancel: () => {
              // 
            },
            onError: (err) => {
              this.paypalRedirect('ERROR', orderRef);
            }
          }).render(this.paypalRef.nativeElement);
        } else {
          console.error("");
        }
      })
      .catch((error) => {
        console.error("", error);
      });

  }

  paypalRedirect(status: string, orderRef: string | null) {
    switch (status) {
      case 'COMPLETED':
        if (orderRef) {
          //this.router.navigate(\['/result/success/' + orderRef\]);
        } else {
          console.error("");
        }
        break;
      case 'ERROR':
        if (orderRef) {
          //this.router.navigate(\['/result/error/' + orderRef\]);
        } else {
          console.error("");
        }
        break;
      default:
        console.error("");
        break;
    }
  }

HTML

<div class="form-group">
                <label>Amount</label>

                <input type="text" ngModel name="myAmount" (ngModelChange)="myAmount=$event"
                    value="$" class="form-control">
            </div>

            <div #paypalRef></div>

This is the design pattern of everything else I have done in the application.

TS

export class NewGameAccountComponent implements OnInit {
 
  newGameAccountState$: Observable<State<CustomHttpResponse<any>>>;
  private dataSubject = new BehaviorSubject<CustomHttpResponse<any>>(null);
  private isLoadingSubject = new BehaviorSubject<boolean>(false);
  isLoading$ = this.isLoadingSubject.asObservable();
  readonly DataState = DataState;
  

  constructor(private gameAccountsService: GameAccountsService) { }

  ngOnInit(): void {
    this.newGameAccountState$ = this.gameAccountsService.activeGameAccounts$()
      .pipe(
        map(response => {
          console.log(response);
          this.dataSubject.next(response);
          return { dataState: DataState.LOADED, appData: response };
        }),
        startWith({ dataState: DataState.LOADING }),
        catchError((error: string) => {
          return of({ dataState: DataState.ERROR, error })
        })
      )
  }
  

  createNewGameAccount(newGameAccountForm: NgForm): void {
    this.isLoadingSubject.next(true);
    this.newGameAccountState$ = this.gameAccountsService.newGameAccount$(newGameAccountForm.value)
      .pipe(
        map(response => {
          console.log(response);
          newGameAccountForm.reset({});
          this.isLoadingSubject.next(false);
          return { dataState: DataState.LOADED, appData: this.dataSubject.value };
        }),
        startWith({ dataState: DataState.LOADED, appData: this.dataSubject.value }),
        catchError((error: string) => {
          this.isLoadingSubject.next(false);
          return of({ dataState: DataState.LOADED, error })
        })
      )
  }

}



HTML

<ng-container *ngIf="(newGameAccountState$ | async) as state" [ngSwitch]="state.dataState">
    <ng-container *ngSwitchCase="DataState.LOADED">
        <app-navbar [user]="state?.appData?.data?.user"></app-navbar>
        <section>
            <div class="container">
               


                <div class="row justify-content-center">
                    <div class="col-md-12">
                        <div class="card">
                            <div class="card-body">
                                <div class="text-center">
                                    <h2><i style="margin-right: 5px;" class="bi bi-person-plus-fill"></i> New Game
                                        Account
                                    </h2>
                                </div>
                    

                                <form #newGameAccountForm="ngForm"
                                    (ngSubmit)="createNewGameAccount(newGameAccountForm)">
                                    ///LOTS OF INPUTS FOR FORM
                                        <button
                                            [disabled]="state.dataState === DataState.LOADING || newGameAccountForm.invalid || newGameAccountForm.pristine|| (isLoading$ | async)"
                                            type="submit" class="btn btn-primary mt-5">
                                            <span *ngIf="isLoading$ | async" class="spinner-border spinner-border-sm"
                                                role="status" aria-hidden="true" style="margin-right: 5px;"></span>
                                            <span *ngIf="isLoading$ | async">Saving...</span>
                                            <span *ngIf="!(isLoading$ | async)">Save Game Account</span>
                                        </button>
                                        
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </ng-container>
</ng-container>

SERVICE

export class GameAccountsService {
  


  private readonly server: string = AppSettings.API_ENDPOINT;

  constructor(private http: HttpClient) { }

activeGameAccounts$ = () => <Observable<CustomHttpResponse<User & GameAccount>>>
    this.http.get<CustomHttpResponse<User & GameAccount>>
      (`${this.server}/user/game_accounts/list_active`)
      .pipe(
        tap(console.log),
        catchError(this.handleError)
      );


 newGameAccount$ = (gameAccount: GameAccount) => <Observable<CustomHttpResponse<User & GameAccount>>>
    this.http.post<CustomHttpResponse<User & GameAccount>>
      (`${this.server}/user/game_account/create`, gameAccount)
      .pipe(
        tap(console.log),
        catchError(this.handleError)
      );

 }
  


interceptor

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  private isTokenRefreshing: boolean = false;
  private refreshTokenSubject: BehaviorSubject<CustomHttpResponse<Profile>> = new BehaviorSubject(null);

  constructor(private userService: UserService) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> | Observable<HttpResponse<unknown>>{
    if(request.url.includes('verify') || request.url.includes('login') || request.url.includes('register') 
            || request.url.includes('refresh') || request.url.includes('resetpassword')|| request.url.includes('reset_password')) {
          return next.handle(request);
      }
    return next.handle(this.addAuthorizationTokenHeader(request, localStorage.getItem(Key.TOKEN)))
      .pipe(
        catchError((error: HttpErrorResponse) => {
          if(error instanceof HttpErrorResponse && error.status === 401 && error.error.reason.includes('expired')) {
            return this.handleRefreshToken(request, next);
          } else {
            return throwError(() => error);
          }
        })
      );
  }

Could someone please help me. Simple stuff like even getting the paypal button to load while using the pattern i used is eluding me. Thank you for reading

Aucun commentaire:

Enregistrer un commentaire