In this blog, we will learn how you can integrate Spartacus - a composable storefront with SAP Commerce to achieve Level 1 B2B Punchout Functionality.
<extension name='b2bpunchout' />
<extension name='b2bpunchoutbackoffice' />
<extension name='b2bpunchoutocc' />
# ---------------------------------------------------------------------------
# Copyright (c) 2022 SAP SE or an SAP affiliate company. All rights reserved.
# ---------------------------------------------------------------------------
INSERT_UPDATE PunchOutCredential ; code[unique = true] ; domain[unique = true] ; identity[unique = true] ; sharedsecret
; NetworkId1 ; NetworkID ; AN01000002779-T ; VerySecret1234$
; AribaSupplier ; NetworkId ; AN01000865920-T ; VerySecret1234$
; DUNS1 ; DUNS ; 123456789 ; VerySecret1234$
; AribaNetworkUserId1 ; AribaNetworkUserId ; sysadmin@ariba.com ; VerySecret1234$
; DUNS2 ; DUNS ; 123 ; VerySecret1234$
INSERT_UPDATE B2BCustomerPunchOutCredentialMapping ; B2BCustomer(uid)[unique = true] ; credentials(code)
; punchout.customer@punchoutorg.com ; NetworkId1,DUNS1
; punchout.customer2@punchoutorg.com ; AribaNetworkUserId1,AribaSupplier
INSERT_UPDATE OAuthClientDetails ; clientId[unique=true] ; resourceIds ; scope ; authorizedGrantTypes ; authorities ; clientSecret ; registeredRedirectUri
; punchout_client ; hybris ; basic ; password,client_credentials ; ROLE_CLIENT ; ;
INSERT_UPDATE B2BCustomer ; description ; customerID ; uid[unique = true] ; originalUid ; email ; name ; title(code) ; groups(uid) ; loginDisabled ; password ; permissionGroups(uid) ; sessionCurrency(isocode)[default = 'USD'] ;
; PunchOut Session Sample Customer ; punchout.customer.session@punchoutorg.com ; punchout.customer.session@punchoutorg.com ; punchout.customer.session@punchoutorg.com ; punchout.customer.session@punchoutorg.com ; PunchOut Customer Session ; mr ; PunchOut Organization, b2bcustomergroup ; false ; pwd4all ; ; ;
INSERT_UPDATE PunchOutCredential ; code[unique = true] ; domain[unique = true] ; identity[unique = true] ; sharedsecret
; DUNSSession ; DUNSSession ; 888888 ; VerySecret1234$
INSERT_UPDATE B2BCustomerPunchOutCredentialMapping ; B2BCustomer(uid)[unique = true] ; credentials(code)
; punchout.customer.session@punchoutorg.com ; DUNSSession
website.powertools-spa.https=http://localhost:4200
website.powertools-spa.http=http://localhost:4200
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginRedirectComponent } from './demo/features/login-redirect/login-redirect.component';
const routes: Routes = [
{
path: 'punchout/cxml/session',
component: LoginRedirectComponent,
},
];
@NgModule({
declarations: [],
imports: [RouterModule.forRoot(routes)],
})
export class DemoRoutingModule {}
import { Component, OnInit } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-login-redirect',
templateUrl: './login-redirect.component.html',
styleUrls: ['./login-redirect.component.scss'],
})
export class LoginRedirectComponent implements OnInit {
protected busy$ = new BehaviorSubject(true);
constructor() {}
ngOnInit(): void {}
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SpinnerModule } from '@spartacus/storefront';
import { LoginRedirectComponent } from './login-redirect.component';
@NgModule({
declarations: [LoginRedirectComponent],
imports: [CommonModule, SpinnerModule],
exports: [LoginRedirectComponent],
})
export class LoginRedirectModule {}
<cx-spinner class="overlay" *ngIf="busy$ | async"></cx-spinner>
{
cartId: '00002159',
token: {
accessToken: 'adss2332ew324324ewr3432432de',
tokenType: 'bearer',
},
userId: 'punchout.customer@punchoutorg.com'
}
}
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { OccEndpointsService } from '@spartacus/core';
import { Observable } from 'rxjs';
export interface PunchOutSession {
customerId: string;
cartId: string;
punchOutLevel: string;
punchOutOperation: string;
selectedItem: string;
token: PunchOutToken;
}
export interface PunchOutToken {
accessToken: String;
tokenType: String;
}
@Injectable({
providedIn: 'root',
})
export class PunchOutService {
constructor(
protected http: HttpClient,
protected occEndpoints: OccEndpointsService
) {}
getPunchOutSessionData(sid: string): Observable<PunchOutSession> {
localStorage.setItem('punchout-sid-key', sid);
const url = this.occEndpoints.buildUrl('getPunchOutAccessToken', {
urlParams: { sid: sid },
});
return this.http.get<PunchOutSession>(url, httpOptions);
}
}
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
}),
};
export const DemoOccEndPoints = {
getPunchOutAccessToken: 'punchout/sessions/${sid}'
};
provideConfig(<OccConfig>{
backend:{
occ:{
endpoints:{
…DemoOccEndPoints
}
}
}
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import { CartActions } from '@spartacus/cart/base/core';
import {
AuthActions,
AuthStorageService,
AuthToken,
BASE_SITE_CONTEXT_ID,
OCC_USER_ID_CURRENT,
RoutingService,
SiteContextParamsService,
StatePersistenceService,
StorageSyncType,
UserIdService,
} from '@spartacus/core';
import { BehaviorSubject, of, Subscription } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { PunchOutService } from '../../services/punchout-service';
@Component({
selector: 'app-login-redirect',
templateUrl: './login-redirect.component.html',
styleUrls: ['./login-redirect.component.scss'],
})
export class LoginRedirectComponent implements OnInit {
protected busy$ = new BehaviorSubject(true);
protected subscription = new Subscription();
constructor(
private activateRoute: ActivatedRoute,
private userIdService: UserIdService,
private store: Store,
private punchoutService: PunchOutService,
private authStorageService: AuthStorageService,
private routing: RoutingService,
private statePersistenceService: StatePersistenceService,
protected siteContextParamsService: SiteContextParamsService
) {}
ngOnInit(): void {
this.activateRoute.queryParams
.pipe(
switchMap((params) => {
return this.punchoutService
.getPunchOutSessionData(params['sid'])
.pipe(
tap((data) => {
if (data.token.accessToken) {
this.statePersistenceService.syncWithStorage({
key: 'cart',
state$: of({
active: data.cartId,
}),
context$: this.siteContextParamsService.getValues([
BASE_SITE_CONTEXT_ID,
]),
storageType: StorageSyncType.LOCAL_STORAGE,
onRead: (state) => this.onRead(state),
});
this.authStorageService.setToken({
access_token: data.token.accessToken,
token_type: data.token.tokenType,
} as AuthToken);
this.userIdService.setUserId(OCC_USER_ID_CURRENT);
this.store.dispatch(new AuthActions.Login());
this.busy$.next(false);
this.routing.go('/');
}
})
);
})
)
.subscribe();
}
protected onRead(state: { active: string } | undefined) {
this.store.dispatch(new CartActions.ClearCartState());
if (state) {
this.store.dispatch(new CartActions.SetActiveCartId(state.active));
} else {
this.store.dispatch(new CartActions.SetActiveCartId(''));
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<cXML payloadID="1391193486192-325467636084519232@216.109.111.62" timestamp="2013-12-19T09:07:19-08:00" xml:lang="en-US">
<Header>
<From>
<Credential domain="NetworkId">
<Identity>AN01000002779-T</Identity>
</Credential>
</From>
<To>
<Credential domain="NetworkID">
<Identity>AN01000002779-T</Identity>
</Credential>
</To>
<Sender>
<Credential domain="AribaNetworkUserId">
<Identity>sysadmin@ariba.com</Identity>
<SharedSecret>VerySecret1234#</SharedSecret>
</Credential>
<UserAgent>Buyer 14s2</UserAgent>
</Sender>
</Header>
<Request>
<PunchOutSetupRequest operation="create">
<BuyerCookie>LwT6nfFQnOvejEKnDZydKtDnG3fq5fes0.2450842054421388232</BuyerCookie>
<Extrinsic name="CostCenter">670</Extrinsic>
<Extrinsic name="UniqueName">catalog_tester</Extrinsic>
<Extrinsic name="UserEmail">catalog_tester@ariba.com</Extrinsic>
<BrowserFormPost>
<URL>https://service.ariba.com/CatalogTester.aw/6218326/ad/handlePunchOutOrder/LwT6nfFQnOvejEKnDZydKtDnG3fq5fes0.24508420544213832232?awr=2&u=bWkn28w2x&awps=ZrbRT213H7OgcFgLfW</URL>
</BrowserFormPost>
<SupplierSetup>
<URL>https://localhost:9002/yacceleratorstorefront/cxml/punchout/new?site=powertools-spa</URL>
</SupplierSetup>
<ShipTo>
<Address addressID="26">
<Name xml:lang="en-US">Catalog Tester</Name>
<PostalAddress name="_5uicbb">
<DeliverTo>Catalog Tester</DeliverTo>
<Street>1234 Catalog Tester Way</Street>
<City>Sunnyvale</City>
<State>CA</State>
<PostalCode>94089</PostalCode>
<Country isoCountryCode="US">United States</Country>
</PostalAddress>
</Address>
</ShipTo>
<SelectedItem>
<ItemID>
<SupplierPartID>AAA</SupplierPartID>
<SupplierPartAuxiliaryID />
</ItemID>
</SelectedItem>
</PunchOutSetupRequest>
</Request>
</cXML>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.051/cXML.dtd">
<cXML payloadID="d868d40c-7bc3-4e3e-96fa-668cc6715ec8" timestamp="2023-04-14T06:29:20Z" xml:lang="en-US">
<Response>
<Status code="200" text="success"/>
<PunchOutSetupResponse>
<StartPage>
<URL>https://localhost:4200/punchout/cxml/session?sid=VeUFhPEfVVPhqNTQ2KGL7tojZkDuyCLXMwbqqqq234343HGs</URL>
</StartPage>
</PunchOutSetupResponse>
</Response>
</cXML>
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
3 | |
3 | |
2 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 |